home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1997 January / macformat46.iso / Shareware Plus / Developers / Library / Grant's CGI Framework / Grant's CGI Framework / grantscgi / Util / CGI.c next >
Encoding:
C/C++ Source or Header  |  1996-09-23  |  88.2 KB  |  3,315 lines

  1. /*****
  2.  *
  3.  *    Grant's CGI Framework
  4.  *        (Common Grant Interface :-)
  5.  *        by Grant Neufeld
  6.  *        http://arpp.carleton.ca/cgi/framework/
  7.  *
  8.  *    CGI.c
  9.  *
  10.  *    Standard functions for cgi applications.
  11.  *
  12.  *    You must call InitCGIUtil in your application startup.
  13.  *    You must install CGIAEHandle as the event handler for the WWWΩsdoc apple event
  14.  *    You must write the function:
  15.  *        void CustomCGIProcess ( CGIHdl theCGIHdl )
  16.  *        which is where you will, guess what, do your application specific processing
  17.  *        of the cgi stuff.
  18.  *
  19.  *    Do not call any functions begining with lower case 'cgi' - you can use any of the
  20.  *    others - but read their comments first for details.
  21.  *
  22.  *    watch the homepage for future upgrades
  23.  *
  24.  *
  25.  *    Copyright ©1995,1996 by Grant Neufeld
  26.  *
  27.  *    gneufeld@ccs.carleton.ca
  28.  *    grant@acm.org
  29.  *  grant@kagi.com
  30.  *
  31.  *    I ask that you let me know of any enhancements (read: bug fixes) to this code.
  32.  *    I would also like copies of (or discounts on) anything you produce this with, please.
  33.  *
  34.  *    See the License and Limited Warranty Agreement for all the legal stuff including
  35.  *    applicable licensing fees.
  36.  *
  37.  *****/
  38.  
  39. #include "MyConfiguration.h"
  40. #if kCompileWithCGICode
  41.  
  42. #define __CGISegment__    1
  43.  
  44. #if __profile__ && __MWERKS__
  45. #include <Profiler.h>
  46. #endif
  47. #if kCompileWithExtraQC
  48. #include <QCAPI.h>
  49. #endif
  50. #include <stdio.h>
  51. #include <string.h>
  52. #include <Threads.h>
  53. #if kCompilingForWSAPI
  54. #include <WSAPI.h>
  55. #endif
  56.  
  57. #if kCompilingForWSAPI
  58. /* normally, "main.c" defines the MainSegment, but for a plug-in we don't use
  59.     "main.c", so we declare "CGI.c" to be the source of the MainSegment */
  60. #define __MainSegment__ 1
  61. #endif
  62.  
  63. #include "globals.h"
  64.  
  65. #if kCompilingForWSAPI
  66. #undef __MainSegment__
  67. #endif
  68.  
  69. #include "AEFunc.h"
  70. #include "CustomHandlers.h"
  71. #include "DebugUtil.h"
  72. #include "LogUtil.h"
  73. #include "MemoryUtil.h"
  74. #include "PrefUtil.h"
  75. #include "ProcessUtil.h"
  76. #include "Quit.h"
  77. #include "StringUtil.h"
  78.  
  79. /* CGI.h processes differently for CGI.c, this is controlled by defining __CGISegment__ */
  80. #include "CGI.h"
  81.  
  82. #undef    __CGISegment__
  83.  
  84.  
  85. /***  CONSTANT DECLARATIONS  ***/
  86.  
  87. #define kHTTPHeaderStrs        10000    // changed from 3000 to support WSAPI
  88. #define kHTTPHeaderOK        1
  89. #define kHTTPHeaderRedirect    2
  90. #define kHTTPHeaderErr        3
  91. #define kHTTPHeaderPush        4
  92.  
  93.  
  94. /***  LOCAL VARIABLES  ***/
  95.  
  96. #if kCompileWithThreadedAppleEvents
  97. static AEEventHandlerUPP    vCGIAEResumeCompleteUPP;
  98. #endif
  99.  
  100. static char *                vNULLString;
  101.  
  102. #if kCompilingForWSAPI
  103.     SInt16                vMyResFile = nil;
  104.     SInt16                vSavedResFile = nil;
  105.     Boolean                vResFileSemaphore = false;
  106. #endif
  107.  
  108.  
  109. /***  LOCAL FUNCTION PROTOTYPES  ***/
  110.  
  111. #if kCompilingForWSAPI
  112.  
  113. static WSAPI_ErrorCode    cgiProcessWSAPI                ( WSAPI_CommandPBPtr );
  114. static void                cgiWSAPILoadParameters        ( CGIHdl );
  115. #if kCompileWithCGImethod
  116. static HTTPMethod        cgiWSAPIGetParamHTTPMethod    ( CGIHdl, WSAPI_ParamKeywords );
  117. #endif
  118.  
  119. #if kCompileWithDebugging
  120. static WSAPI_ErrorCode    cgiWSAPIDebugMessage        ( WSAPI_CommandPBPtr, char * );
  121. #else
  122. #define cgiWSAPIDebugMessage(a,b)    (WSAPI_I_NoErr)
  123. #endif
  124.  
  125. #else
  126.  
  127. pascal OSErr    cgiAESearchDoc                ( AppleEvent *, AppleEvent *, long );
  128. static OSErr    cgiAESearchDocProcess        ( CGIHdl );
  129. static OSErr    cgiAEComplete                ( CGIHdl, AppleEvent * );
  130.  
  131. #if kCompileWithThreadedAppleEvents
  132. pascal void *    cgiAESearchDocProcessThread    ( void * );
  133. pascal OSErr    cgiAEResumeComplete            ( const AppleEvent *, AppleEvent *, long );
  134. #endif
  135.  
  136. #if kCompileWithCGImethod
  137. static    OSErr    cgiAEGetParamHTTPMethod        ( const AppleEvent *, AEKeyword, HTTPMethod *, char *, long );
  138. #endif
  139.  
  140. #if kCompileWithCGISendPartial
  141. p_export OSErr    cgiAESendPartial            ( CGIHdl, char *, long, Boolean );
  142. p_export OSErr    cgiAESendPartialStart        ( CGIHdl, char *, long );
  143. #endif    /* kCompileWithCGISendPartial */
  144.  
  145. #endif
  146.  
  147.  
  148. #if kCompileWithCGIActionSupport || kCompilingForWSAPI
  149. static void        cgiSetRole                    ( CGIHdl );
  150. #endif
  151.  
  152. #if kCompileWithCGIFormHandling
  153.         void    CGIFormFieldsDispose    ( CGIHdl, CGIFormField * );
  154. #endif    /* kCompileWithCGIFormHandling */
  155.  
  156. static void        cgiPostProcess            ( CGIHdl );
  157.  
  158. static void        cgiDisposeRecord        ( CGIHdl );
  159.  
  160.  
  161. /***  FUNCTIONS  ***/
  162.  
  163. /* This initialization function MUST be called in the startup sequence of the
  164.     application if you use any of the CGI functions (including the AppleEvent).
  165.     It will not be called when compiling under WSAPI */
  166. SInt32
  167. InitCGIUtil ( void )
  168. {
  169.     #if kCompilingForWSAPI
  170.     SInt32                theErr;
  171.     #else
  172.     OSErr                theErr;
  173.     AEEventHandlerUPP    theUPP;
  174.     #endif
  175.     #if kCompileWithExtraQC
  176.     QCErr                theQCErr;
  177.     #endif
  178.     
  179.     theErr = noErr;
  180.     
  181.     #if !kCompilingForWSAPI
  182.     /* initialize the null string to consist of one character '\0' */
  183.     vNULLString = (char *) MemoryNewPtr ( 1, &theErr );
  184.     if ( vNULLString != NULL )
  185.     {
  186.         vNULLString[0] = nil;
  187.     }
  188.     else
  189.     {
  190.         /* IMPORTANT: we failed initialization
  191.             if we couldn't allocate a one byte string! */
  192.         if ( theErr == noErr )
  193.         {
  194.             /* make sure there's an error value */
  195.             theErr = memFullErr;
  196.         }
  197.         
  198.         return theErr;
  199.     }
  200.     #endif
  201.     
  202.     GetIndString ( gHTTPHeaderOK, kHTTPHeaderStrs, kHTTPHeaderOK );
  203.     if ( gHTTPHeaderOK[nil] != nil )
  204.     {
  205.         P2CStr ( gHTTPHeaderOK );
  206.         gHTTPHeaderOKSize = strlen ( (char *)gHTTPHeaderOK );
  207.     }
  208.     #if kCompilingForWSAPI
  209.     else
  210.     {
  211.         theErr = WSAPI_E_MessageNotHandled; //••• is this good value to use?
  212.     }
  213.     #endif
  214.     
  215.     GetIndString ( gHTTPHeaderRedirect, kHTTPHeaderStrs, kHTTPHeaderRedirect );
  216.     if ( gHTTPHeaderRedirect[nil] != nil )
  217.     {
  218.         P2CStr ( gHTTPHeaderRedirect );
  219.         gHTTPHeaderRedirectSize = strlen ( (char *)gHTTPHeaderRedirect );
  220.     }
  221.     
  222.     GetIndString ( gHTTPHeaderErr, kHTTPHeaderStrs, kHTTPHeaderErr );
  223.     if ( gHTTPHeaderErr[nil] != nil )
  224.     {
  225.         P2CStr ( gHTTPHeaderErr );
  226.         gHTTPHeaderErrSize = strlen ( (char *)gHTTPHeaderErr );
  227.     }
  228.     
  229.     #if !kCompilingForWSAPI
  230.     
  231.     #if kCompileWithCGISendPartial
  232.     GetIndString ( gHTTPHeaderPush, kHTTPHeaderStrs, kHTTPHeaderPush );
  233.     if ( gHTTPHeaderPush[nil] != nil )
  234.     {
  235.         P2CStr ( gHTTPHeaderPush );
  236.         gHTTPHeaderPushSize = strlen ( (char *)gHTTPHeaderPush );
  237.     }
  238.     #endif
  239.     
  240.     /* it's okay to 'lose track' of theUPP since we never want to get rid of it
  241.         until the application quits - at which point it will automatically be
  242.         disposed of, anyway. */
  243.     theUPP = NewAEEventHandlerProc ( cgiAESearchDoc );
  244.     theErr = AEInstallEventHandler ( kAEClassCGI, kAEIDSearchDoc, theUPP, 0L, false );
  245.     
  246.     #if kCompileWithThreadedAppleEvents
  247.     vCGIAEResumeCompleteUPP = NewAEEventHandlerProc ( cgiAEResumeComplete );
  248.     #endif
  249.     
  250.     #endif /* !kCompilingForWSAPI */
  251.     
  252.     #if kCompileWithExtraQC
  253.     theQCErr = QCBlockBoundsCheckNow ();
  254.     theQCErr = QCHeapCheckNow ();
  255.     #endif
  256.     
  257.     return theErr;
  258. } /* InitCGIUtil */
  259.  
  260.  
  261. /**  MEMORY HANDLING  **/
  262. /* custom wrappers for memory handling under WSAPI.
  263.     these functions get mapped to the normal memory handlers when compiling
  264.     as an AppleEvent CGI application. */
  265. #pragma mark -
  266. #if kCompilingForWSAPI
  267.  
  268. p_export
  269. Handle
  270. CGINewHandle ( CGIHdl theCGIHdl, long theSize, OSErr *outErr )
  271. {
  272.     Handle    theHandle;
  273.     
  274.     my_assert ( theCGIHdl != NULL, "\pCGINewHandle: theCGIHdl is NULL" );
  275.     
  276.     theHandle = (Handle) WSAPI_AllocateOSMemory ( (*theCGIHdl)->wsapi, theSize );
  277.     
  278.     if ( outErr != NULL )
  279.     {
  280.         if ( theHandle == NULL )
  281.         {
  282.             *outErr = memFullErr;
  283.         }
  284.         else
  285.         {
  286.             *outErr = noErr;
  287.         }
  288.     }
  289.     
  290.     return theHandle;
  291. } /* CGINewHandle */
  292.  
  293. p_export
  294. Handle
  295. CGINewHandleClear ( CGIHdl theCGIHdl, long theSize, OSErr *outErr )
  296. {
  297.     Handle    theHandle;
  298.     
  299.     my_assert ( theCGIHdl != NULL, "\pCGINewHandleClear: theCGIHdl is NULL" );
  300.     
  301.     theHandle = (Handle) WSAPI_AllocateOSMemory ( (*theCGIHdl)->wsapi, theSize );
  302.     if ( theHandle == NULL )
  303.     {
  304.         if ( outErr != NULL )
  305.         {
  306.             *outErr = memFullErr;
  307.         }
  308.     }
  309.     else
  310.     {
  311.         if ( outErr != NULL )
  312.         {
  313.             *outErr = noErr;
  314.         }
  315.         /* clear the new handle */
  316.         HLock ( theHandle );
  317.         memset ( *theHandle, nil, theSize );
  318.         HUnlock ( theHandle );
  319.     }
  320.     
  321.     return theHandle;
  322. } /* CGINewHandleClear */
  323.  
  324. p_export
  325. Ptr
  326. CGINewPtr ( CGIHdl theCGIHdl, long theSize, OSErr *outErr )
  327. {
  328.     Ptr        thePtr;
  329.     
  330.     my_assert ( theCGIHdl != NULL, "\pCGINewPtr: theCGIHdl is NULL" );
  331.     
  332.     thePtr = (Ptr) WSAPI_AllocateMemory ( (*theCGIHdl)->wsapi, theSize );
  333.     
  334.     if ( outErr != NULL )
  335.     {
  336.         if ( thePtr == NULL )
  337.         {
  338.             *outErr = memFullErr;
  339.         }
  340.         else
  341.         {
  342.             *outErr = noErr;
  343.         }
  344.     }
  345.     
  346.     return thePtr;
  347. } /* CGINewPtr */
  348.  
  349. p_export
  350. Ptr
  351. CGINewPtrClear ( CGIHdl theCGIHdl, long theSize, OSErr *outErr )
  352. {
  353.     Ptr        thePtr;
  354.     #if kCompileWithExtraQC
  355.     QCErr    theQCErr;
  356.     #endif
  357.     
  358.     my_assert ( theCGIHdl != NULL, "\pCGINewPtrClear: theCGIHdl is NULL" );
  359.     
  360.     thePtr = (Ptr) WSAPI_AllocateMemory ( (*theCGIHdl)->wsapi, theSize );
  361.     if ( thePtr == NULL )
  362.     {
  363.         if ( outErr != NULL )
  364.         {
  365.             *outErr = memFullErr;
  366.         }
  367.     }
  368.     else
  369.     {
  370.         if ( outErr != NULL )
  371.         {
  372.             *outErr = noErr;
  373.         }
  374.         /* clear the new ptr */
  375.         memset ( thePtr, nil, theSize );
  376.     }
  377.     
  378.     #if kCompileWithExtraQC
  379.     theQCErr = QCBlockBoundsCheckNow ();
  380.     theQCErr = QCHeapCheckNow ();
  381.     #endif
  382.     
  383.     return thePtr;
  384. } /* CGINewPtrClear */
  385.  
  386.  
  387. p_export
  388. void
  389. CGIDisposeHandle ( CGIHdl theCGIHdl, Handle theHandle )
  390. {
  391.     my_assert ( theCGIHdl != NULL, "\pCGIDisposeHandle: theCGIHdl is NULL" );
  392.     my_assert ( theHandle != NULL, "\pCGIDisposeHandle: theHandle is NULL" );
  393.     
  394.     WSAPI_FreeOSMemory ( (*theCGIHdl)->wsapi, &theHandle );
  395. } /* CGIDisposeHandle */
  396.  
  397.  
  398. p_export
  399. void
  400. CGIDisposePtr ( CGIHdl theCGIHdl, Ptr thePtr )
  401. {
  402.     my_assert ( theCGIHdl != NULL, "\pCGIDisposePtr: theCGIHdl is NULL" );
  403.     my_assert ( thePtr != NULL, "\pCGIDisposePtr: thePtr is NULL" );
  404.     
  405.     WSAPI_FreeMemory ( (*theCGIHdl)->wsapi, thePtr );
  406. } /* CGIDisposePtr */
  407.  
  408.  
  409. #endif /* kCompilingForWSAPI */
  410.  
  411.  
  412. /**  FORM FIELDS  **/
  413. #pragma mark -
  414. #if kCompileWithCGIFormHandling
  415.  
  416. /* The separator '&' separates individual fields.
  417.     The delimiter '=' delimits the name and value in a field.
  418.     For example: "Field 1=some stuff&Another Field=more stuff&Last Field=no stuff"
  419.     Means that there are 3 fields with names "Field 1", "Another Field" and "Last Field"
  420.     
  421.     The function returns an array of field records with the last containing null values.
  422.     
  423.     You generally shouldn't call this function outside this file.
  424.     Use it at your own risk. */
  425.     /* ••• I should add error reporting */
  426. p_export
  427. CGIFormField *
  428. CGIFormFieldsFromArgs ( CGIHdl theCGIHdl, char *theString, long *count, short *outErr )
  429. {
  430.     CGIFormField *    theFields;
  431.     long            totalStrSize;
  432.     long            totalFields;
  433.     long            nameSize;
  434.     long            valueSize;
  435.     long            currentField;
  436.     char *            theStringPtr;
  437.     char *            fieldSeparator;
  438.     char *            fieldDelimiter;
  439.     #if kCompileWithExtraQC
  440.     QCErr            theQCErr;
  441.     #endif
  442.     
  443.     #if kCompilingForWSAPI
  444.     my_assert ( theCGIHdl != NULL, "\pCGIFormArgs: theCGIHdl is NULL" );
  445.     #endif
  446.     my_assert ( theString != NULL, "\pCGIFormArgs: theString is NULL" );
  447.     
  448.     theFields = NULL;
  449.     
  450.     /* don't return number of fields until function is successful */
  451.     *count = nil;
  452.     
  453.     totalStrSize = strlen ( theString );
  454.     
  455.     /* the total number of fields is the number of separator characters ('&') + 1 */
  456.     totalFields = StringCountChar ( theString, kCGIFormFieldSeparator ) + 1;
  457.     if ( totalFields == 1 )
  458.     {
  459.         /* the case where there were no separator characters is special,
  460.             test for a field delimiter to confirm that the string passed does
  461.             indeed contain field information */
  462.         fieldDelimiter = StringChar ( theString, kCGIFormFieldDelimiter );
  463.         if ( fieldDelimiter == NULL )
  464.         {
  465.             /* string does not contain field data */
  466.             *outErr = 1; /* • might need better error here */
  467.             
  468.             goto Exit_Fail;
  469.         }
  470.     }
  471.     
  472.     theFields = (CGIFormField *) CGINewPtr ( theCGIHdl, ((totalFields + 1) * sizeof(CGIFormField)), outErr );
  473.     if ( theFields == NULL )
  474.     {
  475.         /* memory didn't allocate */
  476.         goto Exit_Fail;
  477.     }
  478.     
  479.     /* set the name and value of the last field in the array to NULL */
  480.     (theFields[totalFields]).name  = NULL;
  481.     (theFields[totalFields]).value = NULL;
  482.     
  483.     theStringPtr = theString;
  484.     
  485.     for ( currentField = nil; currentField < totalFields; currentField++ )
  486.     {
  487.         /* set the name and value of the current field in the array to nil.
  488.             this is to handle errors. */
  489.         (theFields[currentField]).name  = NULL;
  490.         (theFields[currentField]).value = NULL;
  491.         
  492.         fieldDelimiter = StringChar ( theStringPtr, kCGIFormFieldDelimiter );
  493.         fieldSeparator = StringChar ( theStringPtr, kCGIFormFieldSeparator );
  494.         
  495.         /* if there is a field delimiter, and it is before any field separator */
  496.         if ( (fieldDelimiter != NULL) && ((fieldSeparator > fieldDelimiter) ||
  497.             (fieldSeparator == NULL)) )
  498.         {
  499.             /* field name */
  500.             /* the size of the name string is the difference between the begining
  501.                 of the field and the position of the field delimiter */
  502.             nameSize = fieldDelimiter - theStringPtr;
  503.             
  504.             /* allocate the name string */
  505.             (theFields[currentField]).name = (char *) CGINewPtr ( theCGIHdl, nameSize + 1, outErr );
  506.             if ( (theFields[currentField]).name == NULL )
  507.             {
  508.                 /* memory didn't allocate */
  509.                 (theFields[currentField]).value = NULL;
  510.                 
  511.                 goto Exit_Fail;
  512.             }
  513.             
  514.             /* copy the field name */
  515.             BlockMove ( theStringPtr, (theFields[currentField]).name, nameSize );
  516.             /* null terminate the end of the name string */
  517.             ((theFields[currentField]).name)[nameSize] = nil;
  518.             /* convert the url encoded text to a normal string */
  519.             CGIDecodeURLChars ( (theFields[currentField]).name );
  520.             
  521.             /* field value */
  522.             if ( fieldSeparator != NULL )
  523.             {
  524.                 valueSize = fieldSeparator - (fieldDelimiter + 1);
  525.             }
  526.             else
  527.             {
  528.                 valueSize = strlen ( fieldDelimiter + 1 );
  529.             }
  530.             
  531.             (theFields[currentField]).value = (char *) CGINewPtr ( theCGIHdl, (valueSize + 1), outErr );
  532.             if ( (theFields[currentField]).value == NULL )
  533.             {
  534.                 /* memory didn't allocate */
  535.                 CGIDisposePtr ( theCGIHdl, (theFields[currentField]).name );
  536.                 (theFields[currentField]).name = NULL;
  537.                 
  538.                 goto Exit_Fail;
  539.             }
  540.             
  541.             BlockMove ( fieldDelimiter + 1, (theFields[currentField]).value, valueSize );
  542.             ((theFields[currentField]).value)[valueSize] = nil;
  543.             CGIDecodeURLChars ( (theFields[currentField]).value );
  544.             
  545.             theStringPtr = fieldSeparator + 1;
  546.         }
  547.         else
  548.         {
  549.             /* invalid data encountered */
  550.             *outErr = 2; /* •• need better error value */
  551.             
  552.             goto Exit_Fail;
  553.         }
  554.     }
  555.     
  556.     /* assign the return parameters values */
  557.     *count    = totalFields;
  558.     *outErr    = noErr;
  559.     
  560.     #if kCompileWithExtraQC
  561.     theQCErr = QCBlockBoundsCheckNow ();
  562.     #endif
  563.     
  564.     return theFields;
  565.     
  566.     
  567.     Exit_Fail:
  568.     
  569.     if ( theFields != NULL )
  570.     {
  571.         /* release allocated memory */
  572.         CGIFormFieldsDispose ( theCGIHdl, theFields );
  573.     }
  574.     
  575.     #if kCompileWithExtraQC
  576.     theQCErr = QCBlockBoundsCheckNow ();
  577.     theQCErr = QCHeapCheckNow ();
  578.     #endif
  579.     
  580.     return NULL;
  581. } /* CGIFormFieldsFromArgs */
  582.  
  583.  
  584. /* Returns a pointer to the first form field record, in the given fieldArray,
  585.     that's name matches the supplied field name.
  586.     [modified for 1.0b4 - use CGIHdl as parameter instead of CGIFormField] */
  587. p_export
  588. CGIFormField *
  589. CGIFormFieldsFindRecord ( CGIHdl theCGIHdl, const char *fieldName )
  590. {
  591.     CGIFormField    *fieldArray;
  592.     long            currentField;
  593.     short            stringDifference;
  594.     
  595.     my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsFindRecord: theCGIHdl is NULL" );
  596.     my_assert ( fieldName != NULL, "\pCGIFormFieldsFindRecord: fieldName is NULL" );
  597.     
  598.     fieldArray = (*theCGIHdl)->formFields;
  599.     if ( (*theCGIHdl)->formFields == NULL )
  600.     {
  601.         return NULL;
  602.     }
  603.     
  604.     /* look til we find something or we hit the end */
  605.     for ( currentField = nil; (fieldArray[currentField]).name != NULL; currentField++ )
  606.     {
  607.         stringDifference = strcmp ( (fieldArray[currentField]).name, fieldName );
  608.         if ( stringDifference == nil )
  609.         {
  610.             /* found a match, so we're done */
  611.             return &(fieldArray[currentField]);
  612.         }
  613.     }
  614.     
  615.     /* didn't find a match */
  616.     return NULL;
  617. } /* CGIFormFieldsFindRecord */
  618.  
  619.  
  620. /* Returns a pointer to the string containing the value from the field specified
  621.     by 'fieldName'. Returns a pointer to a null string ("") if the field is not
  622.     found. The pointer points directly into the form fields array in theCGIHdl.
  623.     IMPORTANT: Whatever you do - do not attempt to deallocate the string returned
  624.     by this function!!! */
  625. p_export
  626. const char *
  627. CGIFormFieldsFindValue ( CGIHdl theCGIHdl, const char *fieldName )
  628. {
  629.     CGIFormField *    theField;
  630.     
  631.     my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsFindValue: theCGIHdl is NULL" );
  632.     my_assert ( fieldName != NULL, "\pCGIFormFieldsFindValue: fieldName is NULL" );
  633.     
  634.     theField = CGIFormFieldsFindRecord ( theCGIHdl, fieldName );
  635.     if ( (theField == NULL) || (theField->value == NULL) )
  636.     {
  637.         return vNULLString;
  638.     }
  639.     else
  640.     {
  641.         return theField->value;
  642.     }
  643. } /* CGIFormFieldsFindValue */
  644.  
  645.  
  646. /* Deallocate memory for theFields array.
  647.     You generally shouldn't call this function outside this file.
  648.     Use it at your own risk. */
  649. void
  650. CGIFormFieldsDispose ( CGIHdl theCGIHdl, CGIFormField *theFields )
  651. {
  652.     long    offset;
  653.     
  654.     #if kCompilingForWSAPI
  655.     my_assert ( theCGIHdl != NULL, "\pCGIFormFieldsDispose: theCGIHdl is NULL" );
  656.     #else
  657.     #pragma unused(theCGIHdl)
  658.     #endif
  659.     my_assert ( theFields != NULL, "\pCGIFormFieldsDispose: theFields is NULL" );
  660.     
  661.     offset = nil;
  662.     
  663.     do
  664.     {
  665.         if ( (theFields[offset]).name != NULL )
  666.         {
  667.             /* if there's a name string, deallocate its memory */
  668.             CGIDisposePtr ( theCGIHdl, (Ptr)((theFields[offset]).name) );
  669.             
  670.             if ( (theFields[offset]).value != NULL )
  671.             {
  672.                 /* if there's a value string, deallocate its memory */
  673.                 CGIDisposePtr ( theCGIHdl, (Ptr)((theFields[offset]).value) );
  674.             }
  675.         }
  676.         
  677.         offset++;
  678.     } while ( (theFields[offset]).name != NULL );
  679.     
  680.     CGIDisposePtr ( theCGIHdl, (Ptr)theFields );
  681. } /* CGIFormFieldsDispose */
  682.  
  683. #endif    /* kCompileWithCGIFormHandling */
  684.  
  685.  
  686. /**  UTILITIES  **/
  687. #pragma mark -
  688.  
  689. #if kCompileWithCGIActionSupport
  690. /* Returns true if the action parameter is either CGI or ACGI.
  691.     Returns false if it is some other action. */
  692. p_export
  693. Boolean
  694. CGIActionIsCGIorACGI ( CGIHdl theCGIHdl )
  695. {
  696.     int        stringDifference;
  697.     
  698.     my_assert ( theCGIHdl != NULL, "\pCGIActionIsCGIorACGI: theCGIHdl is NULL" );
  699.     
  700.     if ( ((*theCGIHdl)->action)[0] == nil )
  701.     {
  702.         /* if no action is specified, default to CGI as action */
  703.         return true;
  704.     }
  705.     
  706.     /* is the action "CGI"? */
  707.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameCGI );
  708.     if ( stringDifference == nil )
  709.     {
  710.         return true;
  711.     }
  712.     
  713.     /* is the action "ACGI"? */
  714.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameACGI );
  715.     if ( stringDifference == nil )
  716.     {
  717.         return true;
  718.     }
  719.     
  720.     /* didn't find a match */
  721.     return false;
  722. } /* CGIActionIsCGIorACGI */
  723. #endif /* kCompileWithCGIActionSupport */
  724.  
  725.  
  726. /* Test a given path string to ensure it won't try to 'jump' levels.
  727.     thePathString must not be URL encoded (specifically, no %xx hex encoding). */
  728. p_export
  729. Boolean
  730. CGIIsSecurePath ( char *thePathString )
  731. {
  732.     UInt16    currentChar;
  733.     
  734.     my_assert ( thePathString != NULL, "\pCGIIsSecurePath: thePathString is NULL" );
  735.     
  736.     switch ( thePathString[0] )
  737.     {
  738.         case '\0' :
  739.             /* empty string - no security problem */
  740.             return true;
  741.             break;
  742.         
  743.         case '/' :
  744.         case ':' :
  745.             if ( (thePathString[1] == '/') || (thePathString[1] == ':') )
  746.             {
  747.                 /* string begins with '//' or '::' or ':/' or '/:' */
  748.                 return false;
  749.             }
  750.             break;
  751.         
  752.         case '.' :
  753.             if ( (thePathString[1] == '.') &&
  754.                 ((thePathString[2] == '/') || (thePathString[2] == ':')) )
  755.             {
  756.                 /* string begins with '../' or '..:' */
  757.                 return false;
  758.             }
  759.             break;
  760.         
  761.         default :
  762.             break;
  763.     }
  764.     
  765.     currentChar = 1;
  766.     
  767.     while ( thePathString[currentChar] != '\0' )
  768.     {
  769.         switch ( thePathString[currentChar] )
  770.         {
  771.             case '/' :
  772.             case ':' :
  773.                 if ( (thePathString[currentChar+1] == '/') || (thePathString[currentChar+1] == ':') )
  774.                 {
  775.                     /* string contains '//' or '::' or ':/' or '/:' */
  776.                     return false;
  777.                 }
  778.                 break;
  779.             
  780.             case '.' :
  781.                 if ( (thePathString[currentChar+1] == '.') &&
  782.                     ((thePathString[currentChar-1] == '/') || (thePathString[currentChar-1] == ':')) &&
  783.                     ((thePathString[currentChar+2] == '/') || (thePathString[currentChar+2] == ':')) )
  784.                 {
  785.                     /* string contains '/../' or ':..:' or some such */
  786.                     return false;
  787.                 }
  788.                 break;
  789.             
  790.             default :
  791.                 break;
  792.         }
  793.         
  794.         currentChar++;
  795.     }
  796.     
  797.     /* no security leaks encountered */
  798.     return true;
  799. } /* CGIIsSecurePath */
  800.  
  801.  
  802. /* Check to see if the client accessed the server with the correct host name.
  803.     This is only useful for browsers that include the "Host:" field in their
  804.     HTTP request. If no host field is found, (or if the server did not
  805.     supply the full_request data) this function will default to returning
  806.     false (no difference). */
  807. #if kCompileWithCGIfull_request
  808. p_export
  809. Boolean
  810. CGIHostNameDifferent ( CGIHdl theCGIHdl, char *theHostName, short hostNameLength )
  811. {
  812.     char *    hostLine;
  813.     int        stringDiff;
  814.     
  815.     my_assert ( theCGIHdl != NULL, "\pCGIHostNameDifferent: theCGIHdl is NULL" );
  816.     my_assert ( theHostName != NULL, "\pCGIHostNameDifferent: theHostName is NULL" );
  817.     
  818.     /* confirm that the full_request is available */
  819.     if ( ((*theCGIHdl)->full_request == NULL) || (((*theCGIHdl)->full_request)[nil] == nil) )
  820.     {
  821.         /* can't check, so just let client pass */
  822.         return false;
  823.     }
  824.     
  825.     /* search full_request for the Host: field */
  826.     hostLine = strstr ( (*theCGIHdl)->full_request, "\r\nHost: " );
  827.     if ( hostLine == NULL )
  828.     {
  829.         /* can't check, so just let client pass */
  830.         return false;
  831.     }
  832.     
  833.     /* move the ptr to the begining of the host data */
  834.     hostLine += 8;
  835.     
  836.     /* check to see if the Host: field matches */
  837.     stringDiff = strncmp ( hostLine, theHostName, hostNameLength );
  838.     
  839.     return (stringDiff != nil);
  840. } /* CGIHostNameDifferent */
  841. #endif
  842.  
  843.  
  844. /* This is just a preliminary logging function that will log all the data from the cgi */
  845. p_export
  846. void
  847. CGILogData ( CGIHdl theCGIHdl )
  848. {
  849.     #if kCompileWithCGIserver_port || kCompileWithCGISendPartial || kCompileWithCGIFormHandling
  850.     char        tempStr[256];
  851.     #endif
  852.     #if kCompileWithCGIFormHandling
  853.     long        counter;
  854.     #endif
  855.     #if kCompileWithExtraQC
  856.     QCErr        theQCErr;
  857.     #endif
  858.     
  859.     LogStringBreakP ( "\p=== CGI PARAMETERS ===" );
  860.     
  861.     #if kCompileWithCGIpath_args
  862.     if ( (*theCGIHdl)->path_args != NULL )
  863.     {
  864.         LogString ( "path_args:" );
  865.         LogStringBreak ( (*theCGIHdl)->path_args );
  866.     }
  867.     #endif
  868.     
  869.     #if kCompileWithCGIhttp_search_args
  870.     if ( (*theCGIHdl)->http_search_args != NULL )
  871.     {
  872.         LogString ( "http_search_args:" );
  873.         LogStringBreak ( (*theCGIHdl)->http_search_args );
  874.     }
  875.     #endif
  876.     
  877.     #if kCompileWithCGIusername
  878.     LogString ( "username:" );
  879.     LogStringBreak ( (*theCGIHdl)->username );
  880.     #endif
  881.     
  882.     #if kCompileWithCGIpassword
  883.     if ( (*theCGIHdl)->password != NULL )
  884.     {
  885.         LogString ( "password:" );
  886.         LogStringBreak ( (*theCGIHdl)->password );
  887.     }
  888.     #endif
  889.     
  890.     #if kCompileWithCGIfrom_user
  891.     if ( (*theCGIHdl)->from_user != NULL )
  892.     {
  893.         LogString ( "from_user:" );
  894.         LogStringBreak ( (*theCGIHdl)->from_user );
  895.     }
  896.     #endif
  897.     
  898.     #if kCompileWithCGIclient_address
  899.     if ( (*theCGIHdl)->client_address != NULL )
  900.     {
  901.         LogString ( "client_address:" );
  902.         LogStringBreak ( (*theCGIHdl)->client_address );
  903.     }
  904.     #endif
  905.     
  906.     #if kCompileWithCGIpost_args
  907.     if ( (*theCGIHdl)->post_args != NULL )
  908.     {
  909.         LogString ( "post_args:" );
  910.         LogStringBreak ( (*theCGIHdl)->post_args );
  911.     }
  912.     #endif
  913.     
  914.     #if kCompileWithCGImethod
  915.     LogString ( "method:" );
  916.     switch ( (*theCGIHdl)->method )
  917.     {
  918.         case HTTP_get :
  919.             LogStringBreak ( kCGIHTTPMethodGet );
  920.             break;
  921.         
  922.         case HTTP_post :
  923.             LogStringBreak ( kCGIHTTPMethodPost );
  924.             break;
  925.         
  926.         case HTTP_getConditional :
  927.             LogStringBreak ( kCGIHTTPMethodGetConditional );
  928.             break;
  929.         
  930.         default :
  931.             LogStringBreak ( "<undefined>" );
  932.             break;    
  933.     }
  934.     #endif
  935.     
  936.     #if kCompileWithCGIserver_name
  937.     if ( (*theCGIHdl)->server_name != NULL )
  938.     {
  939.         LogString ( "server_name:" );
  940.         LogStringBreak ( (*theCGIHdl)->server_name );
  941.     }
  942.     #endif
  943.     
  944.     #if kCompileWithCGIserver_port
  945.     LogString ( "server_port:" );
  946.     sprintf ( tempStr, "%d", (*theCGIHdl)->server_port );
  947.     LogStringBreak ( tempStr );
  948.     #endif
  949.     
  950.     #if kCompileWithCGIscript_name
  951.     if ( (*theCGIHdl)->script_name != NULL )
  952.     {
  953.         LogString ( "script_name:" );
  954.         LogStringBreak ( (*theCGIHdl)->script_name );
  955.     }
  956.     #endif
  957.     
  958.     #if kCompileWithCGIcontent_type
  959.     if ( (*theCGIHdl)->content_type != NULL )
  960.     {
  961.         LogString ( "content_type:" );
  962.         LogStringBreak ( (*theCGIHdl)->content_type );
  963.     }
  964.     #endif
  965.     
  966.     #if kCompileWithCGIreferer
  967.     if ( (*theCGIHdl)->referer != NULL )
  968.     {
  969.         LogString ( "referer:" );
  970.         LogStringBreak ( (*theCGIHdl)->referer );
  971.     }
  972.     #endif
  973.     
  974.     #if kCompileWithCGIuser_agent
  975.     if ( (*theCGIHdl)->user_agent != NULL )
  976.     {
  977.         LogString ( "user_agent:" );
  978.         LogStringBreak ( (*theCGIHdl)->user_agent );
  979.     }
  980.     #endif
  981.     
  982.     #if kCompileWithCGIActionSupport
  983.     LogString ( "action:" );
  984.     LogStringBreak ( (*theCGIHdl)->action );
  985.     if ( (*theCGIHdl)->action_path != NULL )
  986.     {
  987.         LogString ( "action_path:" );
  988.         LogStringBreak ( (*theCGIHdl)->action_path );
  989.     }
  990.     #endif
  991.     
  992.     #if kCompileWithCGIclient_ip
  993.     LogString ( "client_ip:" );
  994.     LogStringBreak ( (*theCGIHdl)->client_ip );
  995.     #endif
  996.     
  997.     #if kCompileWithCGIfull_request
  998.     if ( (*theCGIHdl)->full_request != NULL )
  999.     {
  1000.         LogString ( "full_request:" );
  1001.         LogStringBreak ( (*theCGIHdl)->full_request );
  1002.     }
  1003.     #endif
  1004.     
  1005.     #if kCompileWithCGISendPartial
  1006.     LogString ( "connection:" );
  1007.     sprintf ( tempStr, "%d", (*theCGIHdl)->connection );
  1008.     LogStringBreak ( tempStr );
  1009.     #endif
  1010.     
  1011.     #if kCompileWithCGIFormHandling
  1012.     LogString ( "totalFields:" );
  1013.     sprintf ( tempStr, "%d", (*theCGIHdl)->totalFields );
  1014.     LogStringBreak ( tempStr );
  1015.     
  1016.     if ( (*theCGIHdl)->totalFields > nil )
  1017.     {
  1018.         LogStringBreak ( "formFields:" );
  1019.         for ( counter = 0; counter < (*theCGIHdl)->totalFields; counter++ )
  1020.         {
  1021.             LogStringP ( "\p\t" );    /* put a tab character at the begining */
  1022.             LogStringAndSeparator ( (((*theCGIHdl)->formFields)[counter]).name, '\t' );
  1023.             LogStringBreak ( (((*theCGIHdl)->formFields)[counter]).value );
  1024.         }
  1025.     }
  1026.     #endif
  1027.     
  1028.     LogStringBreakP ( "\p=== end cgi parameters ===" );
  1029.     
  1030.     #if kCompileWithExtraQC
  1031.     theQCErr = QCBlockBoundsCheckNow ();
  1032.     theQCErr = QCHeapCheckNow ();
  1033.     #endif
  1034. } /* CGILogData */
  1035.  
  1036.  
  1037. /**  CHARACTER CODING  **/
  1038. #pragma mark -
  1039.  
  1040. /* replaces instances of percent signs (%) followed by an ASCII char value
  1041.     with the actual character.
  1042.     This function modifies theString parameter! */
  1043. p_export
  1044. void
  1045. CGIDecodeURLChars ( char *theString )
  1046. {
  1047.     UInt32            read;
  1048.     UInt32            write;
  1049.     unsigned char    theChar;
  1050.     unsigned char    highOrder;
  1051.     unsigned char    lowOrder;
  1052.     Boolean            isValid;
  1053.     
  1054.     my_assert ( theString != NULL, "\pCGIDecodeURLChars: NULL string" );
  1055.     
  1056.     read    = nil;
  1057.     write    = nil;
  1058.     
  1059.     while ( theString[read] != nil )
  1060.     {
  1061.         switch ( theString[read] )
  1062.         {
  1063.             case '%':
  1064.                 /* a percent symbol begins a hex char block (%## where ## is the hex value) */
  1065.                 
  1066.                 isValid = true;
  1067.                 
  1068.                 /* determine high order hex character */
  1069.                 if ( (theString[read+1] >= 'A') && (theString[read+1] <= 'F') )
  1070.                 {
  1071.                     /* uppercase A-F convert to 10-15 */
  1072.                     highOrder = theString[read+1] - 'A' + 10;
  1073.                 }
  1074.                 else if ( (theString[read+1] >= 'a') && (theString[read+1] <= 'f') )
  1075.                 {
  1076.                     /* lowercase a-f convert to 10-15 */
  1077.                     highOrder = theString[read+1] - 'a' + 10;
  1078.                 }
  1079.                 else if ( (theString[read+1] >= '0') && (theString[read+1] <= '9') )
  1080.                 {
  1081.                     /* character digits 0-9 convert to decimal 0-9 */
  1082.                     highOrder = theString[read+1] - '0';
  1083.                 }
  1084.                 else
  1085.                 {
  1086.                     /* Illegal character! Can't convert from hex */
  1087.                     isValid = false;
  1088.                 }
  1089.                 
  1090.                 /* Multiply high order hex digit by 16 */
  1091.                 highOrder *= 16;
  1092.                 
  1093.                 /* determine low order hex character */
  1094.                 if ( (theString[read+2] >= 'A') && (theString[read+2] <= 'F') )
  1095.                 {
  1096.                     /* uppercase A-F convert to 10-15 */
  1097.                     lowOrder = (theString[read+2] - 'A' + 10);
  1098.                 }
  1099.                 else if ( (theString[read+2] >= 'a') && (theString[read+2] <= 'f') )
  1100.                 {
  1101.                     /* lowercase a-f convert to 10-15 */
  1102.                     lowOrder = (theString[read+2] - 'a' + 10);
  1103.                 }
  1104.                 else if ( (theString[read+2] >= '0') && (theString[read+2] <= '9') )
  1105.                 {
  1106.                     /* character digits 0-9 convert to decimal 0-9 */
  1107.                     lowOrder = (theString[read+2] - '0');
  1108.                 }
  1109.                 else
  1110.                 {
  1111.                     /* Illegal character! Can't convert from hex */
  1112.                     isValid = false;
  1113.                 }
  1114.                 
  1115.                 theChar = highOrder + lowOrder;
  1116.                 
  1117.                 if ( isValid )
  1118.                 {
  1119.                     isValid = (theChar >= 0) && (theChar < 256);
  1120.                 }
  1121.                 
  1122.                 if ( isValid )
  1123.                 {
  1124.                     /* if theChar is valid, write it out */
  1125.                     
  1126.                     if ( theChar == 10 )
  1127.                     {
  1128.                         /* don't write newline */
  1129.                         write--;
  1130.                     }
  1131.                     else
  1132.                     {
  1133.                         theString[write] = theChar;
  1134.                     }
  1135.                     
  1136.                     /* Increment read past the two digits of the hex code */
  1137.                     read += 2;
  1138.                 }
  1139.                 else
  1140.                 {
  1141.                     /* invalid hex character code, just write out the percent symbol */
  1142.                     theString[write] = theString[read];
  1143.                 }
  1144.                 break;
  1145.             
  1146.             case '+':
  1147.                 /* Plus symbols convert to space */
  1148.                 theString[write] = ' ';
  1149.                 break;
  1150.                 
  1151.             case 10:
  1152.                 /* ignore line feeds, we only need carriage returns (13) */
  1153.                 write--;
  1154.                 break;
  1155.                 
  1156.             default:
  1157.                 /* write out the character */
  1158.                 theString[write] = theString[read];
  1159.                 break;
  1160.         }
  1161.             
  1162.         read++;
  1163.         write++;
  1164.     }
  1165.     
  1166.     /* terminate the string */
  1167.     theString[write] = '\0';
  1168. } /* CGIDecodeURLChars */
  1169.  
  1170.  
  1171. /* • UPDATE 1996-03-10 •
  1172.     %hex encode all non-alphanumeric characters. IE. '~' (126) becomes '%7E'
  1173.     theString parameter is not modified.
  1174.     Will return NULL if unable to allocate memory for the encoded string.
  1175.     outErr will not be set if it is a NULL ptr. */
  1176. p_export
  1177. char *
  1178. CGIEncodeURLChars ( CGIHdl theCGIHdl, const char *theString, OSErr *outErr )
  1179. {
  1180.     char *            theResult;    /* new string containing the encoded form of theString */
  1181.     long            strSize;    /* size of the source string */
  1182.     Boolean            charNeedsToBeHex;    /* char needs to be encoded as hex */
  1183.     const char *    stringLoc;    /* current location in theString */
  1184.     char *            resultLoc;    /* current location in theResult string */
  1185.     #if kCompileWithExtraQC
  1186.     QCErr            theQCErr;
  1187.     #endif
  1188.     
  1189.     #if kCompilingForWSAPI
  1190.     my_assert ( theCGIHdl != NULL, "\pCGIEncodeURLChars: theCGIHdl is NULL" );
  1191.     #else
  1192.     #pragma unused(theCGIHdl)
  1193.     #endif
  1194.     my_assert ( theString != NULL, "\pCGIEncodeURLChars: theString is NULL" );
  1195.     
  1196.     /* loop through the string finding out how big our final string will need to be.
  1197.         Add two extra bytes for every 'special' character. */
  1198.     strSize = nil;
  1199.     for ( stringLoc = theString; *stringLoc != nil; stringLoc++ )
  1200.     {
  1201.         charNeedsToBeHex = CGICharWillHex ( *stringLoc );
  1202.         if ( charNeedsToBeHex )
  1203.         {
  1204.             /* the character at strOffset is alpha-numeric */
  1205.             strSize++;
  1206.         }
  1207.         else
  1208.         {
  1209.             /* the character at strOffset is special (not alpha-numeric) */
  1210.             strSize += 3;
  1211.         }
  1212.     }
  1213.     
  1214.     /* allocate space for the new encoded string */
  1215.     theResult = (char *) CGINewPtr ( theCGIHdl, (strSize + 1), outErr );
  1216.     if ( theResult != NULL )
  1217.     {
  1218.         /* encode theString into the new encoded string (theResult).
  1219.             loop through the characters of the old string,
  1220.             copying the alpha-numeric chars to the new string,
  1221.             but copying in %XX encoded form when non-alpha-numeric. */
  1222.         for ( stringLoc = theString, resultLoc = theResult; *stringLoc != nil; stringLoc++, resultLoc++ )
  1223.         {
  1224.             charNeedsToBeHex = CGICharWillHex ( *stringLoc );
  1225.             if ( charNeedsToBeHex )
  1226.             {
  1227.                 /* if the character is alphanumeric just copy it */
  1228.                 *resultLoc = *stringLoc;
  1229.             }
  1230.             else
  1231.             {
  1232.                 /* if the character is not alphanumeric, hex encode it */
  1233.                 CGICharToHex ( *stringLoc, resultLoc );
  1234.                 /* add the extra two characters for hex encoding to the result offset */
  1235.                 resultLoc += 2;
  1236.             }
  1237.         }
  1238.         
  1239.         /* terminate the string after the last character written */
  1240.         *resultLoc = nil;
  1241.     }
  1242.     
  1243.     #if kCompileWithExtraQC
  1244.     theQCErr = QCBlockBoundsCheckNow ();
  1245.     theQCErr = QCHeapCheckNow ();
  1246.     #endif
  1247.     
  1248.     return theResult;
  1249. } /* CGIEncodeURLChars */
  1250.  
  1251.  
  1252. /* returns true if theChar will be converted to hex when encoding as an URL string */
  1253. p_export
  1254. Boolean
  1255. CGICharWillHex ( unsigned char theChar )
  1256. {
  1257.     return (
  1258.         ((theChar >= 'a')  && (theChar <= 'z')) ||  /* 0x60 - 0x7A;       a-z   */
  1259.         ((theChar >= '?')  && (theChar <= 'Z')) ||  /* 0x3F - 0x5A; ? @   A-Z   */
  1260.         ((theChar >= '-')  && (theChar <= ':')) ||  /* 0x2D - 0x3A; - . / 0-9 : */
  1261.         ((theChar >= '\"') && (theChar <= '$')) ||  /* 0x22 - 0x24; " # $       */
  1262.          (theChar == '&')  || (theChar == '\'') ||  /* 0x26   0x27; & '         */
  1263.          (theChar == '=') );                         /* 0x3D;        =           */
  1264. } /* CGICharWillHex */
  1265.  
  1266.  
  1267. /* Converts theChar to percent-hex encoding for URLs, and writes that to theString.
  1268.     This assumes that theString is at least 4 bytes long (to have enough room to
  1269.     write the hex encoded text). */
  1270. p_export
  1271. void
  1272. CGICharToHex ( unsigned char theChar, char *theString )
  1273. {
  1274.     unsigned char    chrChunk;
  1275.     
  1276.     /* start off with a percent symbol to indicate a hex character code */
  1277.     theString[0] = '%';
  1278.     
  1279.     /* mask to get the high 4 bits, then shift them into the lower 4 bits */
  1280.     chrChunk = (theChar & 0xF0) >> 4;
  1281.     if ( chrChunk > 9 )
  1282.     {
  1283.         /* chrChunk is a value between A and F in hex */
  1284.         theString[1] = (chrChunk - 10) + 'A';
  1285.     }
  1286.     else
  1287.     {
  1288.         /* chrChunk is a value between 0 and 9 in hex */
  1289.         theString[1] = chrChunk + '0';
  1290.     }
  1291.     
  1292.     /* mask to get the low 4 bits */
  1293.     chrChunk = theChar & 0x0F;
  1294.     if ( chrChunk > 9 )
  1295.     {
  1296.         /* chrChunk is a value between A and F in hex */
  1297.         theString[2] = (chrChunk - 10) + 'A';
  1298.     }
  1299.     else
  1300.     {
  1301.         /* chrChunk is a value between 0 and 9 in hex */
  1302.         theString[2] = chrChunk + '0';
  1303.     }
  1304. } /* CGICharToHex */
  1305.  
  1306.  
  1307. /* convert a '/' delimited path to a ':' delimited one, and make sure there's
  1308.     one ':' at the start if the path includes folders.
  1309.     This function may modify ioPathString.
  1310.     ioPathString MUST have an extra byte after its terminating null byte. */
  1311. p_export
  1312. void
  1313. CGIPathToMacPath ( char *ioPathString )
  1314. {
  1315.     char *    theWorkString;
  1316.     char *    theSlashOffset;
  1317.     
  1318.     /* we start with a directory marker, so we won't need to add one. */
  1319.     if ( (ioPathString[0] == '/') || (ioPathString[0] == ':') )
  1320.     {
  1321.         StringConvertCharToChar ( ioPathString, '/', ':' );
  1322.     }
  1323.     else
  1324.     {
  1325.         /* check if there are any slashes */
  1326.         theSlashOffset = strchr ( ioPathString, '/' );
  1327.         if ( theSlashOffset == NULL )
  1328.         {
  1329.             /* there are no slashes, so nothing no change is needed. */
  1330.         }
  1331.         else
  1332.         {
  1333.             /* find the terminating null byte at the end of the string */
  1334.             theWorkString = strchr ( ioPathString, '\0' );
  1335.             while ( theWorkString >= ioPathString )
  1336.             {
  1337.                 /* the char following the current one is written as a colon if
  1338.                     this one is a slash, or has this char written to it */
  1339.                 theWorkString[1] = (theWorkString[0] == '/')? ':' : theWorkString[0];
  1340.                 theWorkString--;
  1341.             }
  1342.             
  1343.             /* make sure there is a leading colon for the path */
  1344.             ioPathString[0] = ':';
  1345.         }
  1346.     }
  1347. } /* CGIPathToMacPath */
  1348.  
  1349.  
  1350. /***  SENDING DATA  ***/
  1351. #pragma mark -
  1352.  
  1353. /* •new• This function can be used within the context of CustomCGIProcess only.
  1354.     It should be used if you want to return data to the client before you
  1355.     finish processing (especially if processing will take a while), or if you
  1356.     are doing a server-push operation (multipart documents).
  1357.     theCGIHdl's replyData will still be sent upon completion, unless it is null.
  1358.     If you get an error from this function, you can send no more data so should
  1359.     halt processing.
  1360.     
  1361.     If you are compiling as a CGI application, this will implement Send Partial.
  1362.     
  1363.     WARNING: Send Partial support is BUGGY! Fixing it is very high on my list
  1364.     of priorities for the framework.
  1365.      */
  1366. p_export
  1367. OSErr
  1368. CGISendData ( CGIHdl theCGIHdl, void *theData, long dataSize, Boolean sendMore )
  1369. {
  1370.     SInt16    theErr; /* WSAPI_ErrorCode or OSErr */
  1371.     #if kCompileWithExtraQC
  1372.     QCErr    theQCErr;
  1373.     #endif
  1374.     
  1375.     #if kCompilingForWSAPI
  1376.     #pragma unused(sendMore)
  1377.     
  1378.     theErr = WSAPI_SendHTTPData ( (*theCGIHdl)->wsapi, theData, dataSize );
  1379.     if ( (theErr == WSAPI_I_NoErr) && !((*theCGIHdl)->dataSent) )
  1380.     {
  1381.         /* set dataSent so that we'll know that SendData has been used and we
  1382.             won't return an http error header to the client if replyData is
  1383.             null when the CustomCGIProcess function returns. */
  1384.         (*theCGIHdl)->dataSent = true;
  1385.     }
  1386.     
  1387.     #elif kCompileWithCGISendPartial
  1388.     
  1389.     theErr = noErr;
  1390.     
  1391.     if ( !((*theCGIHdl)->sendPartialActive) )
  1392.     {
  1393.         /* theCGIHdl hasn't been used to Send Partial yet - need to start
  1394.             a send partial transaction with the web server */
  1395.         theErr = cgiAESendPartialStart ( theCGIHdl, (char *)theData, dataSize );
  1396.         if ( theErr == noErr )
  1397.         {
  1398.             (*theCGIHdl)->sendPartialActive = true;
  1399.         }
  1400.     }
  1401.     
  1402.     if ( theErr == noErr )
  1403.     {
  1404.         theErr = cgiAESendPartial ( theCGIHdl, (char *)theData, dataSize, sendMore );
  1405.     }
  1406.     
  1407.     if ( !(sendMore) )
  1408.     {
  1409.         (*theCGIHdl)->sendPartialDone = true;
  1410.     }
  1411.     
  1412.     #else
  1413.     
  1414.     #pragma unused(theCGIHdl,theData,dataSize,sendMore)
  1415.     
  1416.     /* CGISendData should not be called unless kCompilingForWSAPI or
  1417.         kCompileWithCGISendPartial */
  1418.     theErr = true;
  1419.     
  1420.     #endif
  1421.     
  1422.     #if kCompileWithExtraQC
  1423.     theQCErr = QCBlockBoundsCheckNow ();
  1424.     theQCErr = QCHeapCheckNow ();
  1425.     #endif
  1426.     
  1427.     return theErr;
  1428. } /* CGISendData */
  1429.  
  1430.  
  1431. #if kCompileWithCGISendPartial && !kCompilingForWSAPI
  1432.  
  1433. /*    From the WebSTAR 1.2 addendum:
  1434.     
  1435.     An ACGI has to inform the WebSTAR server that it intends to
  1436.     return its results in pieces over a period of time, rather than
  1437.     simply lumping it all into the value returned from the WWWWsdoc
  1438.     event. WebSTAR examines the results from an ACGI's reply to see
  1439.     if it matches the string:
  1440.     
  1441.     <SEND_PARTIAL>
  1442.     
  1443.     If this 14 character string is matched exactly (case sensitive),
  1444.     then WebSTAR doesn’t send anything to the client and keeps the
  1445.     connection open until the timeout period expires or WebSTAR
  1446.     receives data via the Send Partial event for that connection.
  1447.     
  1448.     Send Partial events received before the <SEND_PARTIAL> response
  1449.     to the sdoc event are also a legal way to indicate that server push
  1450.     functions are to be performed for a given connection.
  1451.     
  1452.     As long as Send Partial events are received for a given
  1453.     connection, the timeout timer is restarted and the data is sent
  1454.     to the client. If the Send Partial event’s “more” parameter is
  1455.     FALSE, the server closes the connection and assumes that the
  1456.     ACGI has finished sending data.
  1457.     
  1458.     To send events back to WebSTAR from a language like C or Pascal,
  1459.     you must extract and save the “from” Apple event attribute from
  1460.     the reply event sent to your WWWWsdoc handler. The “from” event
  1461.     contains the AEAddressDesc used to address the “SPar” events to
  1462.     WebSTAR.
  1463.     
  1464.     Here's the general flow of events
  1465.     
  1466.     1. A WWW client sends an ACGI URL request to WebSTAR.
  1467.     
  1468.     2. WebSTAR sends a WWWWsdoc event to the ACGI, passing the
  1469.     connection ID.
  1470.     
  1471.     3. The ACGI decides it needs to return data in pieces, so it
  1472.     replies to the sdoc event with the string <SEND_PARTIAL> and
  1473.     saves the connection ID.
  1474.     
  1475.     4. WebSTAR sees that the ACGI wants to return partial data, so
  1476.     it sets a flag indicating that the connection should be checked
  1477.     periodically for timeouts and resets the timer.
  1478.     
  1479.     5. The ACGI sends a Send Partial event to WebSTAR with the first
  1480.     chunk of data, the connection ID, and the “more” flag set to
  1481.     TRUE, indicating more data to come.
  1482.     
  1483.     6. WebSTAR handles the Send Partial event, finds the requested
  1484.     connection, and queues up the data for transmission to the
  1485.     client (allowing the thread owning the connection to schedule
  1486.     the transmission.) WebSTAR then resets the time-out for the
  1487.     connection.
  1488.     
  1489.     Steps 5 and 6 repeat at whatever interval the client decides
  1490.     until the “more” parameter is false. WebSTAR receives the final
  1491.     Send Partial event with the “more” flag set to false, sends the
  1492.     accompanying data, and closes the client connection. */
  1493.  
  1494. /*    The connection ID is found in theCGIHdl as the 'connection' field.
  1495.     Connection is used to identify the particular connection on which to send
  1496.     the data.
  1497.     The data to be sent is pointed to by theData, which is dataSize bytes long.
  1498.     If sendMore is true, there is more data to be sent after processing this
  1499.     event. If it is false, the server will terminate the connection after
  1500.     sending the data from this event.
  1501.     theCGIHdl should not be locked when it is passed to this function. 
  1502.     */
  1503. #pragma segment AppleEvents
  1504. p_export
  1505. OSErr
  1506. cgiAESendPartial ( CGIHdl theCGIHdl, char *theData, long dataSize, Boolean sendMore )
  1507. {
  1508.     OSErr            theErr;
  1509.     DescType        descApp;
  1510.     AEAddressDesc    targetAddress;
  1511.     AppleEvent        theAppleEvent;
  1512.     AppleEvent        reply;
  1513.     short            errNum;
  1514.     DescType        returnedType;
  1515.     long            actualSize;
  1516.     #if kCompileWithExtraQC
  1517.     QCErr                theQCErr;
  1518.     #endif
  1519.     
  1520.     my_assert ( theCGIHdl != NULL, "\pcgiAESendPartial: theCGIHdl is NULL" );
  1521.     my_assert ( theData != NULL, "\pcgiAESendPartial: theData is NULL" );
  1522.     
  1523.     //••• need to change this to /* use the process ID from the server */
  1524.     /* create the application descriptor */
  1525.     descApp    = kAEClassCGI;
  1526.     theErr    = AECreateDesc ( typeApplSignature, &descApp, sizeof(DescType),
  1527.                 &targetAddress );
  1528.     if ( theErr != noErr )
  1529.     {
  1530.         goto EXIT_AESEND;
  1531.     }
  1532.     
  1533.     /* create the apple event record */
  1534.     theErr = AECreateAppleEvent ( kAEClassCGI, kMyAESendPartial, &targetAddress,
  1535.                 kAutoGenerateReturnID, kAnyTransactionID, &theAppleEvent );
  1536.     if ( theErr != noErr )
  1537.     {
  1538.         goto EXIT_AESEND;
  1539.     }
  1540.     
  1541.     /* put the data */
  1542.     theErr = AEPutParamPtr ( &theAppleEvent, kCGIPartialData, typeChar,
  1543.         theData, dataSize );
  1544.     if ( theErr != noErr )
  1545.     {
  1546.         goto EXIT_AESEND;
  1547.     }
  1548.     
  1549.     my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
  1550.         "\pcgiAESendPartial: theCGIHdl is already locked!" );
  1551.     HLockHi ( (Handle)theCGIHdl );
  1552.     /* put the connection id */
  1553.     theErr = AEPutParamPtr ( &theAppleEvent, kConnectionIDKeyword, typeLongInteger,
  1554.                 &((*theCGIHdl)->connection), sizeof(long) );
  1555.     if ( theErr != noErr )
  1556.     {
  1557.         goto EXIT_AESEND;
  1558.     }
  1559.     HUnlock ( (Handle)theCGIHdl );
  1560.     
  1561.     /* put the more value */
  1562.     theErr = AEPutParamPtr ( &theAppleEvent, kMoreKeyword, typeBoolean, &sendMore,
  1563.             sizeof(Boolean) );
  1564.     if ( theErr != noErr )
  1565.     {
  1566.         goto EXIT_AESEND;
  1567.     }
  1568.     
  1569.     /* send the apple event */
  1570.     //••• what to do when reply is delayed?
  1571.     theErr = AESend ( &theAppleEvent, &reply, kAEWaitReply + kAENeverInteract /*5-14*/,
  1572.                 kAENormalPriority, kAEMyTimeoutInTicks, gAEIdleUPP, NULL );
  1573.     if ( theErr != noErr )
  1574.     {
  1575.         goto EXIT_AESEND;
  1576.     }
  1577.     
  1578.     /* extract the error reply */
  1579.     errNum    = noErr;
  1580.     theErr    = AEGetParamPtr ( &reply, keyErrorNumber, typeSMInt, &returnedType, &errNum,
  1581.                 sizeof(errNum), &actualSize );
  1582.     if ( (theErr != noErr) && (theErr != errAEDescNotFound) )
  1583.     {
  1584.         goto EXIT_AESEND;
  1585.     }
  1586.     
  1587.     theErr = errNum;
  1588.  
  1589.     EXIT_AESEND:
  1590.     
  1591.     AEDisposeDesc ( &targetAddress );
  1592.     AEDisposeDesc ( &theAppleEvent );
  1593.     //••• should the reply AppleEvent be disposed of here, too???
  1594.     
  1595.     #if kCompileWithExtraQC
  1596.     theQCErr = QCBlockBoundsCheckNow ();
  1597.     theQCErr = QCHeapCheckNow ();
  1598.     #endif
  1599.     
  1600.     return theErr;
  1601. } /* cgiAESendPartial */
  1602.  
  1603.  
  1604. /* ••• */
  1605. p_export
  1606. OSErr
  1607. cgiAESendPartialStart ( CGIHdl theCGIHdl, char *theData, long dataSize )
  1608. {
  1609. #pragma unused (theCGIHdl,theData,dataSize)
  1610.  
  1611.     OSErr    theErr;
  1612.     
  1613.     theErr = noErr;
  1614.  
  1615.     return theErr;
  1616. } /* cgiAESendPartialStart */
  1617.  
  1618.  
  1619. #endif    /* kCompileWithCGISendPartial && !kCompilingForWSAPI */
  1620.  
  1621.  
  1622. /* ••• This function is incomplete - don't use it!!! */
  1623. p_export
  1624. OSErr
  1625. CGIPassAppleEvent ( CGIHdl theCGIHdl, OSType destinationOSCreator )
  1626. {
  1627.     OSErr            theErr;
  1628.     AEAddressDesc    theAEAddress;
  1629.     AppleEvent        theAppleEvent;
  1630.     
  1631.     my_assert ( theCGIHdl != NULL, "\pCGIPassAppleEvent: theCGIHdl is NULL" );
  1632.     #if !(kCompileWithAssertions)
  1633.     #pragma unused(theCGIHdl)
  1634.     #endif
  1635.     
  1636.     theErr = AECreateDesc ( typeApplSignature, &destinationOSCreator, sizeof(OSType), &theAEAddress );
  1637.     
  1638.     theErr = AECreateAppleEvent ( 'WWWΩ', 'sdoc', &theAEAddress, kAutoGenerateReturnID, kAnyTransactionID, &theAppleEvent );
  1639.     
  1640.     // ••• This function is incomplete - don't use it!!!
  1641.     
  1642.     return theErr;
  1643. } /* CGIPassAppleEvent */
  1644.  
  1645.  
  1646. /**  WebSTAR API SUPPORT  **/
  1647. #pragma mark -
  1648. #if kCompilingForWSAPI
  1649.  
  1650. /* Returns the data for a specific parameter.
  1651.     If buffer is NULL, it will be allocated.
  1652.     buffer is returned if it is passed as non-NULL.
  1653.     bufferSize must be the size in bytes of buffer if it is non-NULL. */
  1654. p_export char *
  1655. CGIWSAPIGetParam ( WSAPI_CommandPBPtr commandPtr, WSAPI_ParamKeywords which,
  1656.     void *buffer, UInt32 bufferSize )
  1657. {
  1658.     WSAPI_DescPtr    theDesc;
  1659.     OSType            dtype;
  1660.     UInt32            dataSize;
  1661.     WSAPI_ErrorCode    err;
  1662.     char            msgString[32];
  1663.     char *            newBuffer;
  1664.     #if kCompileWithExtraQC
  1665.     QCErr            theQCErr;
  1666.     #endif
  1667.     
  1668.     theDesc   = NULL;
  1669.     newBuffer = NULL;
  1670.     
  1671.     if ( buffer != NULL )
  1672.     {
  1673.         my_assert ( bufferSize > nil, "\pCGIWSAPIGetParam: buffer is not NULL, but bufferSize < 1" );
  1674.         
  1675.         ((char*)buffer)[0] = '\0';
  1676.     }
  1677.     
  1678.     /* get the parameter descriptor from the server */
  1679.     err = WSAPI_GetParameter ( commandPtr, which, &theDesc );
  1680.     if ( err != WSAPI_I_NoErr )
  1681.     {
  1682.         WSAPI_DisplayMessage ( commandPtr, "PLUGIN: Error getting parameter" );
  1683.     }
  1684.     else
  1685.     {
  1686.         /* find out how big the data is */
  1687.         dataSize = nil;
  1688.         err = WSAPI_GetDescriptor ( commandPtr, theDesc, &dtype, NULL, &dataSize );
  1689.         if ( err == WSAPI_I_NoErr )
  1690.         {
  1691.             if ( buffer == NULL )
  1692.             {
  1693.                 if (dataSize > nil)
  1694.                 {
  1695.                     /* we need to allocate the buffer based on the size returned.
  1696.                         This is a special case where we make a direct call to the WSAPI. */
  1697.                     newBuffer = (char *) WSAPI_AllocateMemory ( commandPtr, dataSize + 1 );
  1698.                     if ( newBuffer != NULL )
  1699.                     {
  1700.                         /* copy the data into the newBuffer */
  1701.                         err = WSAPI_GetDescriptor ( commandPtr, theDesc, &dtype, newBuffer, &dataSize );
  1702.                         if ( err != WSAPI_I_NoErr )
  1703.                         {
  1704.                             /* This is a special case where we make a direct call to the WSAPI. */
  1705.                             WSAPI_FreeMemory ( commandPtr, newBuffer );
  1706.                             newBuffer = NULL;
  1707.                         }
  1708.                     }
  1709.                     else
  1710.                     {
  1711.                         err = (WSAPI_ErrorCode)memFullErr;
  1712.                     }
  1713.                 }
  1714.                 else
  1715.                 {
  1716.                     /* no data in parameter, so don't bother allocating a buffer */
  1717.                     newBuffer = NULL;
  1718.                 }
  1719.             }
  1720.             else
  1721.             {
  1722.                 if ( dataSize == nil )
  1723.                 {
  1724.                     /* no data in parameter, so don't bother copying it to the buffer */
  1725.                     ((char *)buffer)[0] = nil;
  1726.                     newBuffer = NULL;
  1727.                 }
  1728.                 else if ( bufferSize < dataSize )
  1729.                 {
  1730.                     /* error: the data is too big for the supplied buffer */
  1731.                     err = WSAPI_E_DescriptorTooSmall;
  1732.                 }
  1733.                 else
  1734.                 {
  1735.                     /* copy the data into the buffer */
  1736.                     dataSize = bufferSize;
  1737.                     err = WSAPI_GetDescriptor ( commandPtr, theDesc, &dtype, buffer, &dataSize );
  1738.                     /* we're going to return the buffer */
  1739.                     newBuffer = (char *) buffer;
  1740.                 }
  1741.             }
  1742.         }
  1743.         
  1744.         if ( err != WSAPI_I_NoErr )
  1745.         {
  1746.             WSAPI_DisplayMessage ( commandPtr, "PLUGIN: Error getting data from descriptor" );
  1747.         }
  1748.         else if ( newBuffer != NULL )
  1749.         {
  1750.             if ( dtype == typeChar )
  1751.             {
  1752.                 /* text data, make sure it is terminated */
  1753.                 newBuffer[dataSize] = '\0';
  1754.             }
  1755.         }
  1756.         
  1757.         err = WSAPI_DisposeDescriptor ( commandPtr, &theDesc );
  1758.         if ( err != WSAPI_I_NoErr )
  1759.         {
  1760.             sprintf ( msgString, "Dispose error %d", err );
  1761.             WSAPI_DisplayMessage ( commandPtr, msgString );
  1762.         }
  1763.     }
  1764.     
  1765.     #if kCompileWithExtraQC
  1766.     theQCErr = QCBlockBoundsCheckNow ();
  1767.     theQCErr = QCHeapCheckNow ();
  1768.     #endif
  1769.         
  1770.     return newBuffer;    
  1771. } /* CGIWSAPIGetParam */
  1772.  
  1773.  
  1774. /*  RESOURCE FILE  */
  1775. /* If you need to access resources in the plug-in file's resource fork,
  1776.     you must first call 'CGIWSAPIUseMyResFile'. You must make certain that
  1777.     before you return from a custom function, or make a call to ProcessGiveTime,
  1778.     that you call 'CGIWSAPIResetResFile'.
  1779.     See the WSAPI documentation for an explanation of dealing with resource files. */
  1780.  
  1781. /* Set the plug-in file's resource fork as the current resource file */
  1782. p_export void
  1783. CGIWSAPIUseMyResFile ( void )
  1784. {
  1785.     my_assert ( vMyResFile != nil, "\pCGIWSAPIUseMyResFile: vMyResFile is nil" );
  1786.     my_assert ( !(vResFileSemaphore), "\pCGIWSAPIUseMyResFile: vResFileSemaphore is true" );
  1787.     
  1788.     if ( vResFileSemaphore )
  1789.     {
  1790.         my_assert ( vSavedResFile != nil, "\pCGIWSAPIUseMyResFile: vSavedResFile is nil" );
  1791.     }
  1792.     else
  1793.     {
  1794.         my_assert ( vSavedResFile == nil, "\pCGIWSAPIUseMyResFile: vSavedResFile is not nil" );
  1795.         
  1796.         vSavedResFile = CurResFile ();
  1797.         UseResFile ( vMyResFile );
  1798.         vResFileSemaphore = true;
  1799.     }
  1800. } /* CGIWSAPIUseMyResFile */
  1801.  
  1802. /* Reset the current resource fork to the one in use before the previous call to CGIWSAPIUseMyResFile */
  1803. p_export void
  1804. CGIWSAPIResetResFile ( void )
  1805. {
  1806.     my_assert ( vMyResFile != nil, "\pCGIWSAPIResetResFile: vMyResFile is nil" );
  1807.     my_assert ( vSavedResFile != nil, "\pCGIWSAPIResetResFile: vSavedResFile is nil" );
  1808.     my_assert ( vResFileSemaphore, "\pCGIWSAPIResetResFile: vResFileSemaphore is false" );
  1809.     
  1810.     UseResFile ( vSavedResFile );
  1811.     vSavedResFile = nil;
  1812.     vResFileSemaphore = false;
  1813. } /* CGIWSAPIResetResFile */
  1814.  
  1815.  
  1816. /*  */
  1817. static WSAPI_ErrorCode
  1818. cgiProcessWSAPI ( WSAPI_CommandPBPtr commandPtr )
  1819. {
  1820.     WSAPI_ErrorCode    theErr;
  1821.     CGIHdl            theCGIHdl;
  1822.     #if kCompileWithExtraQC
  1823.     QCErr            theQCErr;
  1824.     #endif
  1825.     
  1826.     /* Allocate the CGIHdl data structure - zeroing out its contents.
  1827.         This is a special case where we make a direct call to the WSAPI. */
  1828.     theCGIHdl = (CGIHdl) WSAPI_AllocateOSMemory ( commandPtr, sizeof(CGIrecord) );
  1829.     if ( theCGIHdl == NULL )
  1830.     {
  1831.         /* memory didn't allocate - can't process cgi */
  1832.         return (WSAPI_ErrorCode)memFullErr;
  1833.     }
  1834.     
  1835.     /* clear the new handle */
  1836.     HLock ( (Handle)theCGIHdl );
  1837.     memset ( *theCGIHdl, nil, sizeof(CGIrecord) );
  1838.     HUnlock ( (Handle)theCGIHdl );
  1839.  
  1840.     
  1841.     (*theCGIHdl)->wsapi = commandPtr;
  1842.     cgiSetRole ( theCGIHdl );
  1843.     
  1844.     cgiWSAPILoadParameters ( theCGIHdl );
  1845.     
  1846.     /* this is where the application specific cgi handling comes into play
  1847.         the function "CustomCGIProcess" must be provided by the user of this source code */
  1848.     CustomCGIProcess ( theCGIHdl );
  1849.     
  1850.     /* complete the sending of data */
  1851.     if ( (*theCGIHdl)->responseData != NULL )
  1852.     {
  1853.         /* there is data remaining to be sent. */
  1854.         
  1855.         HLock ( (Handle)theCGIHdl );
  1856.         
  1857.         #if kCompileWithCGIResponseDataAsHandle
  1858.         HLock ( (*theCGIHdl)->responseData );
  1859.         #endif
  1860.         
  1861.         theErr = WSAPI_SendHTTPData ( commandPtr,
  1862.             #if kCompileWithCGIResponseDataAsHandle
  1863.                 *((*theCGIHdl)->responseData),
  1864.             #else
  1865.                 (*theCGIHdl)->responseData,
  1866.             #endif
  1867.             (*theCGIHdl)->responseSize );
  1868.             
  1869.         #if kCompileWithCGIResponseDataAsHandle
  1870.         HUnlock ( (*theCGIHdl)->responseData );
  1871.         #endif
  1872.         
  1873.         HUnlock ( (Handle)theCGIHdl );
  1874.     }
  1875.     else if ( !((*theCGIHdl)->dataSent) )
  1876.     {
  1877.         /* the user's "CustomCGIProcess" failed to set the responseData
  1878.             and no data has been sent.
  1879.         //    Return an error header. */
  1880.         //theErr = WSAPI_SendHTTPData ( commandPtr,
  1881.         //    "HTTP/1.0 500 Error\r\n\r\n", 22 );
  1882.         //if ( theErr == WSAPI_I_NoErr )
  1883.         //{
  1884.             theErr = WSAPI_E_MessageNotHandled;
  1885.         //}
  1886.     }
  1887.     
  1888.     /* give time, then cgiPostProcess */
  1889.     ProcessGiveTime ( nil, true, theCGIHdl );
  1890.     cgiPostProcess ( theCGIHdl );
  1891.     
  1892.     /* deallocate memory */
  1893.     cgiDisposeRecord ( theCGIHdl );
  1894.     
  1895.     #if kCompileWithExtraQC
  1896.     theQCErr = QCBlockBoundsCheckNow ();
  1897.     theQCErr = QCHeapCheckNow ();
  1898.     #endif
  1899.     
  1900.     return theErr;
  1901. } /* cgiProcessWSAPI */
  1902.  
  1903.  
  1904. /* Load the cgi parameters from the WSAPI into theCGIHdl */
  1905. static void
  1906. cgiWSAPILoadParameters ( CGIHdl theCGIHdl )
  1907. {
  1908.     WSAPI_CommandPBPtr    commandPtr;
  1909.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod && (kCompileWithCGIhttp_search_args || kCompileWithCGIpost_args)
  1910.     short                fieldError;
  1911.     #endif
  1912.     #if kCompileWithExtraQC
  1913.     QCErr                theQCErr;
  1914.     #endif
  1915.     
  1916.     HLockHi ( (Handle)theCGIHdl );
  1917.     
  1918.     commandPtr = (*theCGIHdl)->wsapi;
  1919.     
  1920.     /* the following section is where the parameters are pulled in from
  1921.         the server and assigned to the CGI Handle */
  1922.     
  1923.     /* '----' - direct parameter / path arguments:
  1924.         path_args - arguments to the URL after a $ */
  1925.     #if kCompileWithCGIpath_args
  1926.     (*theCGIHdl)->path_args = CGIWSAPIGetParam ( commandPtr,
  1927.         piPathArgKeyword, (*theCGIHdl)->path_args, NULL );
  1928.     #if kCompileWithCGIAutoDecode
  1929.     if ( (*theCGIHdl)->path_args != NULL )
  1930.     {
  1931.         CGIDecodeURLChars ( (*theCGIHdl)->path_args );
  1932.     }
  1933.     #endif
  1934.     #endif    /* kCompileWithCGIpath_args */
  1935.     
  1936.     /* 'kfor' - search arguments:
  1937.         http_search_args - arguments to the URL after a ? */
  1938.     #if kCompileWithCGIhttp_search_args
  1939.     (*theCGIHdl)->http_search_args = CGIWSAPIGetParam ( commandPtr,
  1940.         piSearchArgKeyword, (*theCGIHdl)->http_search_args, NULL );
  1941.     /* leave decoding until after parsing of form fields */
  1942.     #endif
  1943.     
  1944.     /* 'user' - user name:
  1945.         username - authenticated user name */
  1946.     #if kCompileWithCGIusername
  1947.     CGIWSAPIGetParam ( commandPtr,
  1948.         piUserKeyword, (*theCGIHdl)->username, kCGIMaxusername );
  1949.     #endif
  1950.     
  1951.     /* 'pass' - password:
  1952.         password - authenticated password */
  1953.     #if kCompileWithCGIpassword
  1954.     CGIWSAPIGetParam ( commandPtr,
  1955.         piPasswordKeyword, (*theCGIHdl)->password, kCGIMaxpassword );
  1956.     #endif
  1957.     
  1958.     /* 'frmu' - from user:
  1959.         from_user - non-standard. e-mail address of remote user */
  1960.     #if kCompileWithCGIfrom_user
  1961.     CGIWSAPIGetParam ( commandPtr,
  1962.         piFromUser, (*theCGIHdl)->from_user, kCGIMaxfrom_user );
  1963.     #endif
  1964.     
  1965.     /* 'addr' - client address:
  1966.         client_address - IP address or domain name of remote client's host */
  1967.     #if kCompileWithCGIclient_address
  1968.     CGIWSAPIGetParam ( commandPtr,
  1969.         piAddressKeyword, (*theCGIHdl)->client_address, kCGIMaxclient_address );
  1970.     #endif
  1971.     
  1972.     /* 'post' - post arguments:
  1973.         post_args -  */
  1974.     #if kCompileWithCGIpost_args
  1975.     (*theCGIHdl)->post_args = CGIWSAPIGetParam ( commandPtr,
  1976.         piPostKeyword, (*theCGIHdl)->post_args, NULL );
  1977.     /* leave decoding to after form parsing */
  1978.     #endif
  1979.     
  1980.     /* 'meth' - HTTP method:
  1981.         method - GET, POST, etc. Used to tell if post_args are valid */
  1982.     #if kCompileWithCGImethod
  1983.     (*theCGIHdl)->method = cgiWSAPIGetParamHTTPMethod ( theCGIHdl, piMethodKeyword );
  1984.     #endif
  1985.     
  1986.     /* 'svnm' - server name:
  1987.         server_name - name or IP address of this server */
  1988.     #if kCompileWithCGIserver_name
  1989.     CGIWSAPIGetParam ( commandPtr,
  1990.         piServerName, (*theCGIHdl)->server_name, kCGIMaxserver_name );
  1991.     #endif
  1992.     
  1993.     /* 'svpt' - server port:
  1994.         server_port - TCP/IP port number being used by this server */
  1995.     #if kCompileWithCGIserver_port
  1996.     CGIWSAPIGetParam ( commandPtr,
  1997.         piServerPort, &((*theCGIHdl)->server_port), sizeof(short) );
  1998.     #endif
  1999.     
  2000.     /* 'scnm' - script name:
  2001.         script_name - URL name of this script */
  2002.     #if kCompileWithCGIscript_name
  2003.     (*theCGIHdl)->script_name = CGIWSAPIGetParam ( commandPtr,
  2004.         piScriptName, (*theCGIHdl)->script_name, NULL );
  2005.     #if kCompileWithCGIAutoDecode
  2006.     if ( (*theCGIHdl)->script_name != NULL )
  2007.     {
  2008.         CGIDecodeURLChars ( (*theCGIHdl)->script_name );
  2009.     }
  2010.     #endif
  2011.     #endif
  2012.     
  2013.     /* 'ctyp' - content type:
  2014.         content_type - MIME content type of post_args */
  2015.     #if kCompileWithCGIcontent_type
  2016.     CGIWSAPIGetParam ( commandPtr,
  2017.         piContentType, (*theCGIHdl)->content_type, kCGIMaxcontent_type );
  2018.     #endif
  2019.     
  2020.     /* 'CLen' - content length:
  2021.         content_length - the content-length header field (if any) from HTTP request */
  2022.     #if kCompileWithCGIcontent_length
  2023.     CGIWSAPIGetParam ( commandPtr,
  2024.         piContentLength, &((*theCGIHdl)->content_length), sizeof(long) );
  2025.     #endif
  2026.     
  2027.     /* 'refr' - referer:
  2028.         referer - the URL of the page referencing this document */
  2029.     #if kCompileWithCGIreferer
  2030.     (*theCGIHdl)->referer = CGIWSAPIGetParam ( commandPtr,
  2031.         piRefererKeyword, (*theCGIHdl)->referer, NULL );
  2032.     #endif
  2033.     
  2034.     /* 'Agnt' - user agent:
  2035.         user_agent - the name and version of the WWW client software being used */
  2036.     #if kCompileWithCGIuser_agent
  2037.     CGIWSAPIGetParam ( commandPtr,
  2038.         piUserAgentKeyword, (*theCGIHdl)->user_agent, kCGIMaxuser_agent );
  2039.     #endif
  2040.     
  2041.     /* 'Kact' - action name:
  2042.         action - the name of the action (CGI or ACGI if not a user defined action) */
  2043.     #if kCompileWithCGIActionSupport
  2044.     CGIWSAPIGetParam ( commandPtr,
  2045.         piActionKeyword, (*theCGIHdl)->action, kCGIMaxaction );
  2046.     #endif
  2047.     
  2048.     /* 'Kapt' - action path:
  2049.         action_path - path to the action application */
  2050.     #if kCompileWithCGIActionSupport
  2051.     (*theCGIHdl)->action_path = CGIWSAPIGetParam ( commandPtr,
  2052.         piActionPathKeyword, (*theCGIHdl)->action_path, NULL );
  2053.     #endif
  2054.     
  2055.     /* 'Kcip' - client IP address:
  2056.         client_ip - the IP address of the client */
  2057.     #if kCompileWithCGIclient_ip
  2058.     CGIWSAPIGetParam ( commandPtr,
  2059.         piClientIPAddress, &((*theCGIHdl)->client_ip), sizeof(long) );
  2060.     #endif
  2061.     
  2062.     /* 'Kfrq' - full request:
  2063.         full_request - the full text of the request */
  2064.     #if kCompileWithCGIfull_request
  2065.     (*theCGIHdl)->full_request = CGIWSAPIGetParam ( commandPtr,
  2066.         piFullRequestKeyword, (*theCGIHdl)->full_request, NULL );
  2067.     #endif
  2068.     
  2069.     /* 'Kcid' - connection ID:
  2070.         connection - the ID of the server's connection with a client */
  2071.     #if kCompileWithCGISendPartial
  2072.     CGIWSAPIGetParam ( commandPtr,
  2073.         piConnectionID, &((*theCGIHdl)->connection), sizeof(long) );
  2074.     #endif
  2075.     
  2076.     /* '' - :
  2077.         fileMIMEType -  */
  2078.     #if kCompileWithCGIfileMIMEType
  2079.     (*theCGIHdl)->fileMIMEType = CGIWSAPIGetParam ( commandPtr,
  2080.         piFileMIMEType, (*theCGIHdl)->fileMIMEType, NULL );
  2081.     #endif
  2082.     
  2083.     /* '' - :
  2084.         piServerField -  */
  2085.     #if kCompileWithCGIserverField
  2086.     (*theCGIHdl)->serverField = CGIWSAPIGetParam ( commandPtr,
  2087.         piServerField, (*theCGIHdl)->serverField, NULL );
  2088.     #endif
  2089.     
  2090.     /* '' - :
  2091.         serverDirectoryPath -  */
  2092.     #if kCompileWithCGIserverDirectoryPath
  2093.     (*theCGIHdl)->serverDirectoryPath = CGIWSAPIGetParam ( commandPtr,
  2094.         piServerDirectoryPath, (*theCGIHdl)->serverDirectoryPath, NULL );
  2095.     #endif
  2096.     
  2097.     /* '' - :
  2098.         urlPhysicalPath -  */
  2099.     #if kCompileWithCGIurlPhysicalPath
  2100.     (*theCGIHdl)->urlPhysicalPath = CGIWSAPIGetParam ( commandPtr,
  2101.         piURLPhysicalPath, (*theCGIHdl)->urlPhysicalPath, NULL );
  2102.     #endif
  2103.     
  2104.     /* '' - :
  2105.         ifModifiedSince -  */
  2106.     #if kCompileWithCGIifModifiedSince
  2107.     CGIWSAPIGetParam ( commandPtr,
  2108.         piIfModifiedSince, &((*theCGIHdl)->ifModifiedSince), sizeof(short) );
  2109.     #endif
  2110.     
  2111.     /* '' - :
  2112.         currentRealm -  */
  2113.     #if kCompileWithCGIcurrentRealm
  2114.     CGIWSAPIGetParam ( commandPtr,
  2115.         piCurrentRealm, (*theCGIHdl)->currentRealm, 256 );
  2116.     #endif
  2117.     
  2118.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod
  2119.     /* separate the form fields into an array */
  2120.     switch ( (*theCGIHdl)->method )
  2121.     {
  2122.         #if kCompileWithCGIhttp_search_args
  2123.         case HTTP_get :
  2124.             if ( (*theCGIHdl)->http_search_args != NULL )
  2125.             {
  2126.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs ( theCGIHdl,
  2127.                     (*theCGIHdl)->http_search_args, &((*theCGIHdl)->totalFields),
  2128.                     &fieldError );
  2129.             }
  2130.             break;
  2131.         #endif
  2132.         
  2133.         #if kCompileWithCGIpost_args
  2134.         case HTTP_post :
  2135.             if ( (*theCGIHdl)->post_args != NULL )
  2136.             {
  2137.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs ( theCGIHdl,
  2138.                     (*theCGIHdl)->post_args, &((*theCGIHdl)->totalFields), &fieldError );
  2139.             }
  2140.             break;
  2141.         #endif
  2142.     }
  2143.     #endif    /* kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod */
  2144.     
  2145.     /* now that the possible need to use them for form fields is over, we can
  2146.         decode the search args */
  2147.     #if kCompileWithCGIAutoDecode && kCompileWithCGIhttp_search_args
  2148.     if ( (*theCGIHdl)->http_search_args != NULL )
  2149.     {
  2150.         CGIDecodeURLChars ( (*theCGIHdl)->http_search_args );
  2151.     }
  2152.     #endif
  2153.     
  2154.     #if kCompileWithExtraQC
  2155.     theQCErr = QCBlockBoundsCheckNow ();
  2156.     theQCErr = QCHeapCheckNow ();
  2157.     #endif
  2158.     
  2159.     HUnlock ( (Handle)theCGIHdl );
  2160. } /* cgiWSAPILoadParameters */
  2161.  
  2162.  
  2163. #if kCompileWithCGImethod
  2164. /* private function to get an HTTPMethod from an AppleEvent parameter */
  2165. static HTTPMethod
  2166. cgiWSAPIGetParamHTTPMethod ( CGIHdl theCGIHdl, WSAPI_ParamKeywords which )
  2167. {
  2168.     HTTPMethod    theMethod;
  2169.     char *        tempBuffer;
  2170.     int            stringDiff;
  2171.     
  2172.     /* default to GET */
  2173.     theMethod = HTTP_get;
  2174.     
  2175.     tempBuffer = CGIWSAPIGetParam ( (*theCGIHdl)->wsapi, which, NULL, NULL );
  2176.     if ( tempBuffer != NULL )
  2177.     {
  2178.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodPost );
  2179.         if ( stringDiff == nil )
  2180.         {
  2181.             theMethod = HTTP_post;
  2182.             goto EXIT_HTTP_METHOD;
  2183.         }
  2184.         
  2185.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGet );
  2186.         if ( stringDiff == nil )
  2187.         {
  2188.             theMethod = HTTP_get;
  2189.             goto EXIT_HTTP_METHOD;
  2190.         }
  2191.         
  2192.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodConditionalGet );
  2193.         if ( stringDiff == nil )
  2194.         {
  2195.             theMethod = HTTP_getConditional;
  2196.             goto EXIT_HTTP_METHOD;
  2197.         }
  2198.         
  2199.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGetConditional );
  2200.         if ( stringDiff == nil )
  2201.         {
  2202.             theMethod = HTTP_getConditional;
  2203.             goto EXIT_HTTP_METHOD;
  2204.         }
  2205.     }
  2206.     
  2207.     EXIT_HTTP_METHOD:
  2208.     
  2209.     if ( tempBuffer != NULL )
  2210.     {
  2211.         CGIDisposePtr ( theCGIHdl, tempBuffer );
  2212.     }
  2213.     
  2214.     return theMethod;
  2215. } /* cgiWSAPIGetParamHTTPMethod */
  2216. #endif /* kCompileWithCGImethod */
  2217.  
  2218.  
  2219. /* Write messages to the WebSTAR display console.
  2220.     Only operates when compiling in debug mode */
  2221. #if kCompileWithDebugging
  2222. static WSAPI_ErrorCode
  2223. cgiWSAPIDebugMessage ( WSAPI_CommandPBPtr commandPtr, char *theMessage )
  2224. {
  2225.     WSAPI_ErrorCode    theErr;
  2226.     
  2227.     theErr = WSAPI_DisplayMessage ( commandPtr, theMessage );
  2228.     
  2229.     return theErr;
  2230. } /* cgiWSAPIDebugMessage */
  2231. #endif
  2232.  
  2233. /* This routine must be implemented if compiling for WSAPI.
  2234.     This is only to be called from the main routine in the WSAPI_Lib. */
  2235. WSAPI_ErrorCode
  2236. WSAPI_Dispatch ( WSAPI_CommandPBPtr commandPtr )
  2237. {
  2238.     WSAPI_ErrorCode    theErr;
  2239.     char            theString[80];
  2240.     
  2241.     /* check that the API versions match */
  2242.     if ( commandPtr->api_version < WSAPI_VERSION )
  2243.     {
  2244.         /* the server is probably too old and doesn't support the
  2245.             calls used by this plug-in */
  2246.         return WSAPI_E_MessageNotHandled;
  2247.     }
  2248.     else
  2249.     { 
  2250.         theErr = WSAPI_I_NoErr;
  2251.         
  2252.         switch ( commandPtr->command )
  2253.         {
  2254.             case WSAPI_Register:
  2255.                 my_assert ( (strlen(kMyCGIName)) < WSAPI_NAME_SIZE,
  2256.                     "\pWSAPI_Dispatch:WSAPI_Register: kMyCGIName (\"MyConfiguration.h\") is too long" );
  2257.                 
  2258. //                vMyResFile = nil;
  2259. //                vSavedResFile = nil;
  2260.                 
  2261.                 /* copy the plugin-name to the WSAPI command paramBlock */
  2262.                 strcpy ( commandPtr->param.init.pluginName, kMyCGIName );
  2263.                 
  2264.                 /* register the plug-in with the server */
  2265.                 theErr = CustomCGIWSAPIRegister ( commandPtr );
  2266.                 break;
  2267.                 
  2268.             case WSAPI_Init:
  2269.                 vMyResFile = commandPtr->param.init.resRef;
  2270.                 vSavedResFile = nil;
  2271.                 
  2272.                 my_assert ( vMyResFile != nil, "\pWSAPI_Dispatch:WSAPI_Init: vMyResFile is nil" );
  2273.                 
  2274.                 theErr = cgiWSAPIDebugMessage ( commandPtr, kMyCGIName ": WSAPI_Init event" );
  2275.                 
  2276.                 /* ••• determine gProcessFSSpec */
  2277. //                •••
  2278.                 
  2279.                 CGIWSAPIUseMyResFile ();
  2280.                 
  2281.                 PrefStartup ();
  2282.                 theErr = (WSAPI_ErrorCode) InitCGIUtil ();
  2283.                 
  2284.                 /* call the custom initialization */
  2285.                 theErr = (WSAPI_ErrorCode) CustomStartup ( commandPtr );
  2286.                 
  2287.                 CGIWSAPIResetResFile ();
  2288.                 
  2289.                 #if kCompileWithPeriodicTask
  2290.                 if ( theErr == WSAPI_I_NoErr )
  2291.                 {
  2292.                     /* request idle time so that periodic task can execute */
  2293.                     theErr = WSAPI_RequestIdleTime ( commandPtr, kSleepTimeForPeriodicTask );
  2294.                 }
  2295.                 #endif
  2296.                 break;
  2297.                 
  2298.             case WSAPI_Shutdown :
  2299. //                theErr = cgiWSAPIDebugMessage ( commandPtr, kMyCGIName ": WSAPI_Shutdown event" );
  2300.                 
  2301.                 theErr = (WSAPI_ErrorCode) QuitWSAPI ( commandPtr );
  2302.                 break;
  2303.                 
  2304.             case WSAPI_Idle :
  2305. //                theErr = cgiWSAPIDebugMessage ( commandPtr, kMyCGIName ": WSAPI_Idle event" );
  2306.                 
  2307.                 /* do any global processing on a regular basis here,
  2308.                     outside the context of a specific connection with
  2309.                     a HTTP client. */
  2310.                 #if kCompileWithPeriodicTask
  2311.                 ProcessPeriodicTask ();
  2312.                 #endif
  2313.                 break;
  2314.                 
  2315.             case WSAPI_Run:
  2316. //                theErr = cgiWSAPIDebugMessage ( commandPtr, kMyCGIName ": WSAPI_Run event" );
  2317.                 
  2318.                 /* CGI processing starts here */
  2319.                 switch ( commandPtr->param.run.role )
  2320.                 {
  2321.                     case WSAPI_CGI_Role :
  2322.                         /* Handle the CGI Event */
  2323.                     case WSAPI_PreProcessor_Role :
  2324.                         /* acting as a pre-processor */
  2325.                     case WSAPI_Error_Role :
  2326.                         /* handling bad URL requests */
  2327.                     case WSAPI_NoAccess_Role :
  2328.                         /* handling requests denied by IP or domain */
  2329.                     case WSAPI_Index_Role :
  2330.                         /* handling a request for the default file */
  2331.                         
  2332.                         CGIWSAPIUseMyResFile ();
  2333.                         
  2334.                         theErr = cgiProcessWSAPI ( commandPtr );
  2335.                         
  2336.                         CGIWSAPIResetResFile ();
  2337.                         break;
  2338.                     
  2339.                     /* PostProcessor, Security and Logging roles have no
  2340.                         client to talk to, so are quite limited in what they
  2341.                         are allowed to do. Refer to the WSAPI docs. */
  2342.                     case WSAPI_PostProcessor_Role :
  2343.                         /* acting as a post-processor */
  2344.                     case WSAPI_Logging_Role :
  2345.                         /* receiving log info */
  2346.                     case WSAPI_Security_Role :
  2347.                         /* performing an access authorization */
  2348.                             
  2349.                     default :
  2350.                         sprintf ( theString, kMyCGIName ": called with unhandled role: %d", commandPtr->param.run.role );
  2351.                         WSAPI_DisplayMessage ( commandPtr, theString );
  2352.                         break;
  2353.                 }
  2354.                 break;
  2355.                 
  2356.             case WSAPI_Emergency:
  2357.                 /* Some sort of emergency has arisen, requiring the plug-in
  2358.                     to do it's best to free up memory, release resources,
  2359.                     close files, etc. */
  2360.                 CustomEmergencyHandler ();
  2361.                 break;
  2362.                 
  2363.             case WSAPI_ServerStateChanged :
  2364.                 /* one or more of the server's settings have changed.
  2365.                     Take appropriate action if you
  2366.                     depend on specific settings values. */
  2367. //                theErr = cgiWSAPIDebugMessage ( commandPtr, kMyCGIName ": WSAPI_ServerStateChanged event" );
  2368.                 break;
  2369.                 
  2370.             default:
  2371.                 sprintf ( theString, kMyCGIName ": received bad message, %d", commandPtr->command );
  2372.                 theErr = WSAPI_DisplayMessage ( commandPtr, theString );
  2373.                 theErr = WSAPI_E_MessageNotHandled;
  2374.                 break;
  2375.         }
  2376.     }
  2377.     
  2378.     return theErr;
  2379. } /* WSAPI_Dispatch */
  2380.  
  2381.  
  2382. #else /* not kCompilingForWSAPI */
  2383. /**  APPLE EVENT SUPPORT  **/
  2384. #pragma mark -
  2385.  
  2386. /* AppleEvent Handler for the CGI WWWΩ-sdoc event */
  2387. pascal OSErr
  2388. cgiAESearchDoc ( AppleEvent *theAppleEvent, AppleEvent *theReply, long Reference )
  2389. {
  2390. #pragma unused (Reference)
  2391.     OSErr        theErr;
  2392.     CGIHdl        theCGIHdl;
  2393.     #if kCompileWithThreadedAppleEvents
  2394.     ThreadID    theThread;
  2395.     #endif
  2396.     #if kCompileWithDebugLogging
  2397.     char        tempStr[16];
  2398.     #endif
  2399.     #if kCompileWithExtraQC
  2400.     QCErr                theQCErr;
  2401.     #endif
  2402.     
  2403.     /* we're handling a CGI event, so we're more busy now */
  2404.     ProcessIsMoreBusy ();
  2405.     
  2406.     /* reset 'quit on idle time' timer */
  2407.     ResetQuitIdleTimer();
  2408.     
  2409.     #if kCompileWithDebugLogging
  2410.     LogStringP ( "\p==================\rCGI Event Recieved\rTICKS: " );
  2411.     sprintf ( tempStr, "%d", TickCount() );
  2412.     LogStringBreak ( tempStr );
  2413.     LogStringBreakP ( "\p------------------" );
  2414.     #endif
  2415.     
  2416.     /* Allocate the CGIHdl data structure - zeroing out its contents. */
  2417.         /* it's okay to use NULL here because we're not compiling for WSAPI,
  2418.             so theCGIHdl that would normally go where NULL is isn't needed */
  2419.     theCGIHdl = (CGIHdl) CGINewHandleClear ( NULL, sizeof(CGIrecord), &theErr );
  2420.     if ( theCGIHdl == NULL )
  2421.     {
  2422.         /* memory didn't allocate - can't process cgi */
  2423.         //••• may be better to try to return the http error header here
  2424.         return theErr;
  2425.     }
  2426.     
  2427.     /* copy the apple event and reply records */
  2428.     (*theCGIHdl)->appleEvent = *theAppleEvent;
  2429.     (*theCGIHdl)->replyEvent = *theReply;
  2430. //    HLock ( (Handle)theCGIHdl );
  2431. //    theErr = AEDuplicateDesc ( theAppleEvent, &((*theCGIHdl)->appleEvent) );
  2432.     //• handle err?
  2433. //    theErr = AEDuplicateDesc ( theReply, &((*theCGIHdl)->replyEvent) );
  2434.     //• handle err?
  2435. //    HUnlock ( (Handle)theCGIHdl );
  2436.     
  2437.     #if kCompileWithThreadedAppleEvents
  2438.     #if kCompileWithThreadsOptional
  2439.     if ( gHasThreadMgr )
  2440.     {
  2441.     #endif
  2442.         
  2443.         /* It is necessary to suspend the AppleEvent in order to thread its
  2444.             processing because of some real weirdness with AEProcessAppleEvent
  2445.             not being "reentrant." This means you can't be processing multiple
  2446.             Apple Events at the same time, so they have to be 'suspended' if
  2447.             you want to deal with more than one (IE. multi-threaded processing). */
  2448.         theErr = AESuspendTheCurrentEvent ( theAppleEvent );
  2449.         if ( theErr == noErr)
  2450.         {
  2451.             (*theCGIHdl)->suspended = true;
  2452.             /* AppleEvent has been suspended, so we can spawn a thread for processing. */
  2453.             theErr = ThreadNewThreadFromPool ( cgiAESearchDocProcessThread,
  2454.                 theCGIHdl, (void**)NULL, &theThread );
  2455.         }
  2456.         
  2457.     #if kCompileWithThreadsOptional
  2458.     }
  2459.     #endif
  2460.     #endif /* kCompileWithThreadedAppleEvents */
  2461.     
  2462.     #if kCompileWithThreadedAppleEvents
  2463.     #if kCompileWithThreadsOptional
  2464.     if ( !gHasThreadMgr || (theErr != noErr) )
  2465.     #else
  2466.     if ( theErr != noErr )
  2467.     #endif
  2468.     {
  2469.     #endif
  2470.         
  2471.         /* if threading isn't available, or the attempt to thread failed,
  2472.             or the attempt to suspend the AppleEvent failed,
  2473.             process the Apple Event without threading. */ 
  2474.         theErr = cgiAESearchDocProcess ( theCGIHdl );
  2475.         
  2476.     #if kCompileWithThreadedAppleEvents
  2477.     }
  2478.     else
  2479.     {
  2480.         /* we suspended the AE, and spawned the thread, now let's start it. */
  2481. //•••        YieldToThread ( theThread );
  2482.     }
  2483.     #endif
  2484.     
  2485.     #if kCompileWithExtraQC
  2486.     theQCErr = QCBlockBoundsCheckNow ();
  2487.     theQCErr = QCHeapCheckNow ();
  2488.     #endif
  2489.     
  2490.     return theErr;
  2491. } /* cgiAESearchDoc */
  2492.  
  2493.  
  2494. /* Entry point for cgi handler thread. */
  2495. #if kCompileWithThreadedAppleEvents
  2496. pascal void *
  2497. cgiAESearchDocProcessThread ( void *threadParam )
  2498. {
  2499.     OSErr        theErr;
  2500.     ThreadID    currentThread;
  2501.     
  2502.     my_assert ( threadParam != NULL,
  2503.         "\pCGIAESearchDocProcessThread: the CGI handle (threadParam) is NULL" );
  2504.     
  2505.     /* The threadParam is used to pass the CGIHdl. */
  2506.     theErr = cgiAESearchDocProcess ( (CGIHdl)threadParam );
  2507.     
  2508.     /* Find the ID of current thread and use DisposeThread to dispose of it so
  2509.         that my custom thread termination procedure will be used to recycle
  2510.         this thread's allocation for the thread pool. */
  2511.     GetCurrentThread ( ¤tThread );
  2512.     DisposeThread ( currentThread, (void *)theErr, true );
  2513.     
  2514.     /* This line below is actually irrelevant, since the DisposeThread call above
  2515.         will result in the immediate termination of this thread.
  2516.         I keep it in because a return result is needed for the compiler not to
  2517.         issue a warning (and I have the "treat all warnings as errors" flag set
  2518.         in my compiler, like every programmer should). */
  2519.     return (void *)theErr;
  2520. } /* cgiAESearchDocProcessThread */
  2521. #endif
  2522.  
  2523. /* Process the CGI WWWΩ-sdoc event.
  2524.     theCGIHdl must be valid (non-nil) and unlocked.
  2525.     Responsible for ensuring that cgiAEComplete is called so CGIDisposeHandle
  2526.     will be called. */
  2527. static OSErr
  2528. cgiAESearchDocProcess ( CGIHdl theCGIHdl )
  2529. {
  2530.     OSErr            theErr;
  2531.     Ptr                tempBuffer;
  2532.     AppleEvent *    theAppleEvent;
  2533.     /* the 'fieldError' variable is only used if forms with auto-processing are on,
  2534.         the method parameter is used, and one or both of the http_search_args and
  2535.         post_args are used */
  2536.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod && (kCompileWithCGIhttp_search_args || kCompileWithCGIpost_args)
  2537.     short        fieldError;
  2538.     #endif
  2539.     #if kCompileWithExtraQC
  2540.     QCErr                theQCErr;
  2541.     #endif
  2542.     
  2543.     my_assert ( theCGIHdl != NULL, "\pcgiAESearchDocProcess: theCGIHdl is NULL" );
  2544.     
  2545.     #if __profile__ && __MWERKS__
  2546.     gProfileOn = !( ProfilerInit( collectDetailed, bestTimeBase, 20, 5 ) );
  2547.     #endif
  2548.     
  2549.     /* reset 'quit on idle time' timer */
  2550.     ResetQuitIdleTimer();
  2551.         
  2552.     tempBuffer = CGINewPtr ( theCGIHdl, kCGIParamMaxSize, &theErr );
  2553.     if ( tempBuffer == NULL )
  2554.     {
  2555.         goto Exit_Complete;
  2556.     }
  2557.     
  2558.     my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
  2559.         "\pcgiAESearchDocProcess: theCGIHdl is already locked!" );
  2560.     HLockHi ( (Handle)theCGIHdl );
  2561.     
  2562.     /* copy the AppleEvent record into a local variable for faster access. */
  2563.     theAppleEvent = &((*theCGIHdl)->appleEvent);
  2564.     
  2565.     /* the following section is where the parameters are pulled from the CGI
  2566.         Apple Event and allocated in the CGI Handle */
  2567.     
  2568.     /* '----' - direct parameter:
  2569.         path_args - arguments to the URL after a $ */
  2570.     #if kCompileWithCGIpath_args
  2571.     theErr = AEGetParamString ( theAppleEvent, '----', &((*theCGIHdl)->path_args),
  2572.         (char *)tempBuffer, kCGIParamMaxSize );
  2573.     #if kCompileWithCGIAutoDecode
  2574.     if ( theErr == noErr )
  2575.     {
  2576.         CGIDecodeURLChars ( (*theCGIHdl)->path_args );
  2577.     }
  2578.     #endif
  2579.     #endif    /* kCompileWithCGIpath_args */
  2580.     
  2581.     /* 'kfor' - search arguments:
  2582.         http_search_args - arguments to the URL after a ? */
  2583.     #if kCompileWithCGIhttp_search_args
  2584.     theErr = AEGetParamString ( theAppleEvent, kCGIhttp_search_args,
  2585.         &((*theCGIHdl)->http_search_args), (char *)tempBuffer, kCGIParamMaxSize );
  2586.     /* leave decoding to after parsing of form fields */
  2587.     #endif
  2588.     
  2589.     /* 'user' - user name:
  2590.         username - authenticated user name */
  2591.     #if kCompileWithCGIusername
  2592.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIusername,
  2593.         (*theCGIHdl)->username, kCGIMaxusername );
  2594.     #endif
  2595.     
  2596.     /* 'pass' - password:
  2597.         password - authenticated password */
  2598.     #if kCompileWithCGIpassword
  2599.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIpassword,
  2600.         (*theCGIHdl)->password, kCGIMaxpassword );
  2601.     #endif
  2602.     
  2603.     /* 'frmu' - from user:
  2604.         from_user - non-standard. e-mail address of remote user */
  2605.     #if kCompileWithCGIfrom_user
  2606.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIfrom_user,
  2607.         (*theCGIHdl)->from_user, kCGIMaxfrom_user );
  2608.     #endif
  2609.     
  2610.     /* 'addr' - client address:
  2611.         client_address - IP address or domain name of remote client's host */
  2612.     #if kCompileWithCGIclient_address
  2613.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIclient_address,
  2614.         (*theCGIHdl)->client_address, kCGIMaxclient_address );
  2615.     #endif
  2616.     
  2617.     /* 'post' - post arguments:
  2618.         post_args -  */
  2619.     #if kCompileWithCGIpost_args
  2620.     theErr = AEGetParamString ( theAppleEvent, kCGIpost_args,
  2621.         &((*theCGIHdl)->post_args), (char *)tempBuffer, kCGIParamMaxSize );
  2622.     /* leave decoding to after form parsing */
  2623.     #endif
  2624.     
  2625.     /* 'meth' - HTTP method:
  2626.         method - GET, POST, etc. Used to tell if post_args are valid */
  2627.     #if kCompileWithCGImethod
  2628.     theErr = cgiAEGetParamHTTPMethod ( theAppleEvent, kCGImethod,
  2629.         &((*theCGIHdl)->method), (char *)tempBuffer, kCGIParamMaxSize );
  2630.     #endif
  2631.     
  2632.     /* 'svnm' - server name:
  2633.         server_name - name or IP address of this server */
  2634.     #if kCompileWithCGIserver_name
  2635.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIserver_name,
  2636.         (*theCGIHdl)->server_name, kCGIMaxserver_name );
  2637.     #endif
  2638.     
  2639.     /* 'svpt' - server port:
  2640.         server_port - TCP/IP port number being used by this server */
  2641.     #if kCompileWithCGIserver_port
  2642.     theErr = AEGetParamShort ( theAppleEvent, kCGIserver_port,
  2643.         &((*theCGIHdl)->server_port), (char *)tempBuffer, kCGIParamMaxSize );
  2644.     #endif
  2645.     
  2646.     /* 'scnm' - script name:
  2647.         script_name - URL name of this script */
  2648.     #if kCompileWithCGIscript_name
  2649.     theErr = AEGetParamString ( theAppleEvent, kCGIscript_name,
  2650.         &((*theCGIHdl)->script_name), (char *)tempBuffer, kCGIParamMaxSize );
  2651.     #if kCompileWithCGIAutoDecode
  2652.     if ( theErr == noErr )
  2653.     {
  2654.         CGIDecodeURLChars ( (*theCGIHdl)->script_name );
  2655.     }
  2656.     #endif
  2657.     #endif
  2658.     
  2659.     /* 'ctyp' - content type:
  2660.         content_type - MIME content type of post_args */
  2661.     #if kCompileWithCGIcontent_type
  2662.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIcontent_type,
  2663.         (*theCGIHdl)->content_type, kCGIMaxcontent_type );
  2664.     #endif
  2665.     
  2666.     /* 'refr' - referer:
  2667.         referer - the URL of the page referencing this document */
  2668.     #if kCompileWithCGIreferer
  2669.     theErr = AEGetParamString ( theAppleEvent, kCGIreferer,
  2670.         &((*theCGIHdl)->referer), (char *)tempBuffer, kCGIParamMaxSize );
  2671.     #endif
  2672.     
  2673.     /* 'Agnt' - user agent:
  2674.         user_agent - the name and version of the WWW client software being used */
  2675.     #if kCompileWithCGIuser_agent
  2676.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIuser_agent,
  2677.         (*theCGIHdl)->user_agent, kCGIMaxuser_agent );
  2678.     #endif
  2679.     
  2680.     /* 'Kact' - action name:
  2681.         action - the name of the action (CGI or ACGI if not a user defined action) */
  2682.     #if kCompileWithCGIActionSupport
  2683.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIaction,
  2684.         (*theCGIHdl)->action, kCGIMaxaction );
  2685.     #endif
  2686.     
  2687.     /* 'Kapt' - action path:
  2688.         action_path - path to the action application */
  2689.     #if kCompileWithCGIActionSupport
  2690.     theErr = AEGetParamString ( theAppleEvent, kCGIaction_path,
  2691.         &((*theCGIHdl)->action_path), (char *)tempBuffer, kCGIParamMaxSize );
  2692.     #endif
  2693.     
  2694.     /* 'Kcip' - client IP address:
  2695.         client_ip - the IP address of the client */
  2696.     #if kCompileWithCGIclient_ip
  2697.     theErr = AEGetParamStringNoAlloc ( theAppleEvent, kCGIclient_ip,
  2698.         (*theCGIHdl)->client_ip, kCGIMaxclient_ip );
  2699.     #endif
  2700.     
  2701.     /* 'Kfrq' - full request:
  2702.         full_request - the full text of the request */
  2703.     #if kCompileWithCGIfull_request
  2704.     theErr = AEGetParamString ( theAppleEvent, kCGIfull_request,
  2705.         &((*theCGIHdl)->full_request), (char *)tempBuffer, kCGIParamMaxSize );
  2706.     #endif
  2707.     
  2708.     /* 'Kcid' - connection ID:
  2709.         connection - the ID of the server's connection with a client */
  2710.     #if kCompileWithCGISendPartial
  2711.     theErr = AEGetParamLong ( theAppleEvent, kCGIconnection, &((*theCGIHdl)->connection) );
  2712.     #endif
  2713.     
  2714.     /* don't need the buffer any more */
  2715.     CGIDisposePtr ( theCGIHdl, tempBuffer );
  2716.     
  2717.     /* don't need to take the time to check for required parameters because
  2718.         the official CGI policy (as set by Chuck Shotton) says that all
  2719.         parameters are to be optional. Otherwise, we would call:
  2720.         theErr = AEFuncGotRequiredParams ( theAppleEvent ); */
  2721.     
  2722.     #if kCompileWithCGIActionSupport
  2723.     /* determine the role of the CGI */
  2724.     cgiSetRole ( theCGIHdl );
  2725.     #endif
  2726.     
  2727.     #if kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod
  2728.     /* separate the form fields into an array */
  2729.     switch ( (*theCGIHdl)->method )
  2730.     {
  2731.         #if kCompileWithCGIhttp_search_args
  2732.         case HTTP_get :
  2733.             if ( (*theCGIHdl)->http_search_args != NULL )
  2734.             {
  2735.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs ( theCGIHdl,
  2736.                     (*theCGIHdl)->http_search_args, &((*theCGIHdl)->totalFields),
  2737.                     &fieldError );
  2738.             }
  2739.             break;
  2740.         #endif
  2741.         
  2742.         #if kCompileWithCGIpost_args
  2743.         case HTTP_post :
  2744.             if ( (*theCGIHdl)->post_args != NULL )
  2745.             {
  2746.                 (*theCGIHdl)->formFields = CGIFormFieldsFromArgs ( theCGIHdl,
  2747.                     (*theCGIHdl)->post_args, &((*theCGIHdl)->totalFields), &fieldError );
  2748.             }
  2749.             break;
  2750.         #endif
  2751.     }
  2752.     #endif    /* kCompileWithCGIFormHandling && kCompileWithCGIFormAutoProcess && kCompileWithCGImethod */
  2753.     
  2754.     /* now that the possible need to use them for form fields is over, we can
  2755.         decode the search args */
  2756.     #if kCompileWithCGIAutoDecode && kCompileWithCGIhttp_search_args
  2757.     if ( (*theCGIHdl)->http_search_args != NULL )
  2758.     {
  2759.         CGIDecodeURLChars ( (*theCGIHdl)->http_search_args );
  2760.     }
  2761.     #endif
  2762.     
  2763.     HUnlock ( (Handle)theCGIHdl );
  2764.     
  2765.     #if kCompileWithDebugLogging
  2766.     CGILogData ( theCGIHdl );
  2767.     #endif
  2768.     
  2769.     /* this is where the application specific cgi handling comes into play
  2770.         the function "CustomCGIProcess" must be provided by the user of this source code */
  2771.     CustomCGIProcess ( theCGIHdl );
  2772.     
  2773.     Exit_Complete:
  2774.     
  2775.     #if __profile__ && __MWERKS__
  2776.     if ( gProfileOn )
  2777.     {
  2778.         ProfilerDump ( "\p" kProfileNameStr "-" kProcessorString ".prof" );
  2779.         ProfilerTerm ();
  2780.     }
  2781.     #endif
  2782.     
  2783.     #if kCompileWithThreadedAppleEvents
  2784.     if ( (*theCGIHdl)->suspended )
  2785.     {
  2786.         //••• might need to do something special when doing send partial
  2787.         
  2788.         /* We're in a suspended AppleEvent, so we'll need to resume the
  2789.             AppleEvent to have it complete and return the reply properly. */
  2790.         theErr = AEResumeTheCurrentEvent ( &((*theCGIHdl)->appleEvent),
  2791.             &((*theCGIHdl)->replyEvent), vCGIAEResumeCompleteUPP, (long)theCGIHdl );
  2792.         if ( theErr != noErr )
  2793.         {
  2794.             theErr = AEResumeTheCurrentEvent ( &((*theCGIHdl)->appleEvent),
  2795.                 &((*theCGIHdl)->replyEvent), vCGIAEResumeCompleteUPP, (long)NULL );
  2796.         }
  2797.     }
  2798.     else
  2799.     {
  2800.     #endif
  2801.         
  2802.         /* We weren't suspended, but still need to take care of the AppleEvent
  2803.             reply record. */
  2804.         theErr = cgiAEComplete ( theCGIHdl, &((*theCGIHdl)->replyEvent) );
  2805.         
  2806.     #if kCompileWithThreadedAppleEvents
  2807.     }
  2808.     #endif
  2809.     
  2810.     /* reset 'quit on idle time' timer */
  2811.     ResetQuitIdleTimer();
  2812.     
  2813.     #if kCompileWithExtraQC
  2814.     theQCErr = QCBlockBoundsCheckNow ();
  2815.     theQCErr = QCHeapCheckNow ();
  2816.     #endif
  2817.     
  2818.     return theErr;
  2819. } /* cgiAESearchDocProcess */
  2820.  
  2821.  
  2822. /* Call the event completion function (cgiAEComplete) when resuming
  2823.      suspended AppleEvents. theReference must be a CGIHdl.
  2824.      The result returned from this function will be used as the result of the AppleEvent. */
  2825. #if kCompileWithThreadedAppleEvents
  2826. pascal OSErr
  2827. cgiAEResumeComplete ( const AppleEvent *theAppleEvent, AppleEvent *theReply, long theReference )
  2828. {
  2829. #pragma unused (theAppleEvent,theReply)
  2830.     OSErr    theErr;
  2831.     
  2832.     my_assert ( theReference != nil,
  2833.         "\pCGIAEResumeComplete: the CGI handle (theReference) is nil" );
  2834.     
  2835.     theErr = cgiAEComplete ( (CGIHdl)theReference, theReply );
  2836.         
  2837.     return theErr;
  2838. } /* cgiAEResumeComplete */
  2839. #endif
  2840.  
  2841. /* Complete the CGI AppleEvent. theCGIHdl must be valid. */
  2842. static OSErr
  2843. cgiAEComplete ( CGIHdl theCGIHdl, AppleEvent *theReply )
  2844. {
  2845.     OSErr    theErr;
  2846.     #if kCompileWithDebugLogging
  2847.     char        tempStr[16];
  2848.     #endif
  2849.     #if kCompileWithExtraQC
  2850.     QCErr                theQCErr;
  2851.     #endif
  2852.     
  2853.     my_assert ( theCGIHdl != NULL, "\pcgiAEComplete: theCGIHdl is NULL" );
  2854.     my_assert ( (HGetState((Handle)theCGIHdl) & kMemoryHandleLockedFlag) == nil,
  2855.         "\pcgiAEComplete: theCGIHdl is already locked!" );
  2856.     
  2857.     HLock ( (Handle)theCGIHdl );
  2858.     
  2859.     #if kCompileWithCGISendPartial
  2860.     if ( (*theCGIHdl)->sendPartialActive )
  2861.     {
  2862.         /* we're using sendpartial */
  2863.         if ( !((*theCGIHdl)->sendPartialDone) )
  2864.         {
  2865.             if ( (*theCGIHdl)->responseData != NULL )
  2866.             {
  2867.                 /* close off the send partial with the last of the custom responseData */
  2868.                 theErr = CGISendData ( theCGIHdl, (*theCGIHdl)->responseData, (*theCGIHdl)->responseSize, false );
  2869.             }
  2870.             else 
  2871.             {
  2872.                 /* Need to close off the send partial with a !sendMore parameter in 
  2873.                     a send partial event */
  2874.                 theErr = CGISendData ( theCGIHdl, NULL, nil, false );
  2875.             }
  2876.         }
  2877.         else
  2878.         {
  2879.             /* nothing to do since all of the data has been sent and the send partial
  2880.                 connection has been closed */
  2881.         }
  2882.     }
  2883.     else
  2884.     {
  2885.     #endif /* kCompileWithCGISendPartial */
  2886.     
  2887.         if ( (*theCGIHdl)->responseData != NULL )
  2888.         {
  2889.             #if kCompileWithCGIActionSupport
  2890.             if ( (*theCGIHdl)->role != CGIRole_PostProcessor )
  2891.             {
  2892.                 /* only return data if not in the PostProcessor role */
  2893.             #endif
  2894.                 
  2895.                 #if kCompileWithCGIResponseDataAsHandle
  2896.                 my_assert ( (HGetState((Handle)((*theCGIHdl)->responseData)) & kMemoryHandleLockedFlag) == nil,
  2897.                     "\pcgiAEComplete: (*theCGIHdl)->responseData is already locked!" );
  2898.                 HLock ( (*theCGIHdl)->responseData );
  2899.                 #endif
  2900.                 
  2901.                 /* If the user's "CustomCGIProcess" function set the responseData properly,
  2902.                     return it. */
  2903.                 theErr = AEPutParamPtr ( theReply, keyDirectObject, typeChar,
  2904.                     #if kCompileWithCGIResponseDataAsHandle
  2905.                         *((*theCGIHdl)->responseData),
  2906.                     #else
  2907.                         (Ptr)((*theCGIHdl)->responseData),
  2908.                     #endif
  2909.                     (*theCGIHdl)->responseSize );
  2910.                     
  2911.                 #if kCompileWithCGIResponseDataAsHandle
  2912.                 HUnlock ( (*theCGIHdl)->responseData );
  2913.                 #endif
  2914.                 
  2915.             #if kCompileWithCGIActionSupport
  2916.             }
  2917.             #endif
  2918.         }
  2919.         else
  2920.         {
  2921.             #if kCompileWithCGIActionSupport
  2922.             switch ( (*theCGIHdl)->role )
  2923.             {
  2924.                 case CGIRole_PostProcessor :
  2925.                 case CGIRole_Logging :    //• not sure this is where this belongs
  2926.                     /* do nothing */
  2927.                     break;
  2928.                 
  2929.                 case CGIRole_PreProcessor :
  2930.                 case CGIRole_Security :    //• not sure this is where this belongs
  2931.                 case CGIRole_Error :    //• not sure this is where this belongs
  2932.                 case CGIRole_NoAccess :    //• not sure this is where this belongs
  2933.                 case CGIRole_Index :    //• not sure this is where this belongs
  2934.                     /* return an empty string to indicate no data */
  2935.                     theErr = AEPutParamPtr ( theReply, keyDirectObject, typeChar, "", 0 );
  2936.                     break;
  2937.                 
  2938.                 case CGIRole_CGI :
  2939.                 default :
  2940.             #endif
  2941.                     
  2942.                     /* if the user's "CustomCGIProcess" failed to set the responseData properly,
  2943.                         return an error header. */
  2944.                     theErr = AEPutParamPtr ( theReply, keyDirectObject, typeChar,
  2945.                         (Ptr)gHTTPHeaderErr, gHTTPHeaderErrSize );
  2946.                     
  2947.             #if kCompileWithCGIActionSupport
  2948.                     break;
  2949.             }
  2950.             #endif
  2951.         }
  2952.         
  2953.     #if kCompileWithCGISendPartial
  2954.     }
  2955.     #endif /* kCompileWithCGISendPartial */
  2956.     
  2957.     HUnlock ( (Handle)theCGIHdl );
  2958.     
  2959.     /* give time, then cgiPostProcess */
  2960.     ProcessGiveTime ( nil, true, theCGIHdl );
  2961.     cgiPostProcess ( theCGIHdl );
  2962.     
  2963.     #if kCompileWithDebugLogging
  2964.     CGILogData ( theCGIHdl );
  2965.     #endif
  2966.     
  2967.     /* deallocate memory */
  2968.     cgiDisposeRecord ( theCGIHdl );
  2969.         
  2970.     #if kCompileWithDebugLogging
  2971.     LogStringP ( "\p___________________\rCGI Event Completed\rTICKS: " );
  2972.     sprintf ( tempStr, "%d", TickCount() );
  2973.     LogStringBreak ( tempStr );
  2974.     LogStringBreakP ( "\p===================" );
  2975.     #endif
  2976.     
  2977.     /* we're done this CGI event, so we're less busy now */
  2978.     ProcessIsLessBusy ();
  2979.     
  2980.     #if kCompileWithExtraQC
  2981.     theQCErr = QCBlockBoundsCheckNow ();
  2982.     theQCErr = QCHeapCheckNow ();
  2983.     #endif
  2984.     
  2985.     return theErr;
  2986. } /* cgiAEComplete */
  2987.  
  2988.  
  2989. #if kCompileWithCGImethod
  2990. /* private function to get an HTTPMethod from an AppleEvent parameter */
  2991. static OSErr
  2992. cgiAEGetParamHTTPMethod ( const AppleEvent *theAppleEvent, AEKeyword theAEKeyword, HTTPMethod *theMethod, char *tempBuffer, long bufferSize )
  2993. {
  2994.     OSErr        theErr;
  2995.     DescType    actualType;
  2996.     Size        actualSize;
  2997.     int            stringDiff;
  2998.     
  2999.     my_assert ( theMethod != NULL, "\pcgiAEGetParamHTTPMethod: theMethod ptr is NULL" );
  3000.     my_assert ( theAppleEvent != NULL, "\pcgiAEGetParamHTTPMethod: theAppleEvent ptr is NULL" );
  3001.     my_assert ( tempBuffer != NULL, "\pcgiAEGetParamHTTPMethod: tempBuffer ptr is NULL" );
  3002.     
  3003.     theErr = AEGetParamPtr ( theAppleEvent, theAEKeyword, typeChar,
  3004.         &actualType, (Ptr)tempBuffer, bufferSize, &actualSize );
  3005.     if ( theErr == noErr )
  3006.     {
  3007.         my_assert ( actualSize <= bufferSize,
  3008.             "\pcgiAEGetParamHTTPMethod: actual param size too big" );
  3009.         
  3010.         /* terminate the buffer with a null byte */
  3011.         tempBuffer[actualSize] = nil;
  3012.         
  3013.         /* compare the buffer with constants to determine the http method used */
  3014.         
  3015.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodPost );
  3016.         if ( stringDiff == nil )
  3017.         {
  3018.             *theMethod = HTTP_post;
  3019.             
  3020.             return noErr;
  3021.         }
  3022.         
  3023.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGet );
  3024.         if ( stringDiff == nil )
  3025.         {
  3026.             *theMethod = HTTP_get;
  3027.             
  3028.             return noErr;
  3029.         }
  3030.         
  3031.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodConditionalGet );
  3032.         if ( stringDiff == nil )
  3033.         {
  3034.             *theMethod = HTTP_getConditional;
  3035.             
  3036.             return noErr;
  3037.         }
  3038.         
  3039.         stringDiff = strcmp ( tempBuffer, kCGIHTTPMethodGetConditional );
  3040.         if ( stringDiff == nil )
  3041.         {
  3042.             *theMethod = HTTP_getConditional;
  3043.             
  3044.             return noErr;
  3045.         }
  3046.     }
  3047.     
  3048.     *theMethod = HTTP_UNDEFINED;
  3049.     
  3050.     return theErr;
  3051. } /* cgiAEGetParamHTTPMethod */
  3052. #endif
  3053.  
  3054.  
  3055. #endif /* not kCompilingForWSAPI */
  3056.  
  3057.  
  3058. /**  MISC  **/
  3059. #pragma mark -
  3060.  
  3061. /* set the role field of theCGIHdl */
  3062. #if kCompileWithCGIActionSupport ||  kCompilingForWSAPI
  3063. static void
  3064. cgiSetRole ( CGIHdl theCGIHdl )
  3065. {
  3066.     #if !(kCompilingForWSAPI)
  3067.     int        stringDifference;
  3068.     #endif
  3069.     
  3070.     my_assert ( theCGIHdl != NULL, "\pcgiSetRole: theCGIHdl is NULL" );
  3071.     
  3072.     #if kCompilingForWSAPI
  3073.     
  3074.     my_assert ( (*theCGIHdl)->wsapi->command == WSAPI_Run, "\pcgiSetRole: not in run mode" );
  3075.     
  3076.     (*theCGIHdl)->role = (CGIRole)((*theCGIHdl)->wsapi->param.run.role);
  3077.     
  3078.     #else /* if not kCompilingForWSAPI */
  3079.     
  3080.     if ( (*theCGIHdl)->action == NULL )
  3081.     {
  3082.         /* no action defined - set role to CGI */
  3083.         (*theCGIHdl)->role = CGIRole_CGI;
  3084.         return;
  3085.     }
  3086.     
  3087.     /* CGI */
  3088.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameCGI );
  3089.     if ( stringDifference == nil )
  3090.     {
  3091.         (*theCGIHdl)->role = CGIRole_CGI;
  3092.         return;
  3093.     }
  3094.     
  3095.     /* ACGI */
  3096.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameACGI );
  3097.     if ( stringDifference == nil )
  3098.     {
  3099.         (*theCGIHdl)->role = CGIRole_CGI;
  3100.         return;
  3101.     }
  3102.     
  3103.     /* PreProcessor */
  3104.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNamePreProcessor );
  3105.     if ( stringDifference == nil )
  3106.     {
  3107.         (*theCGIHdl)->role = CGIRole_PreProcessor;
  3108.         return;
  3109.     }
  3110.     
  3111.     /* PostProcessor */
  3112.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNamePostProcessor );
  3113.     if ( stringDifference == nil )
  3114.     {
  3115.         (*theCGIHdl)->role = CGIRole_PostProcessor;
  3116.         return;
  3117.     }
  3118.     
  3119.     /* Logging */
  3120.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameLogging );
  3121.     if ( stringDifference == nil )
  3122.     {
  3123.         (*theCGIHdl)->role = CGIRole_Logging;
  3124.         return;
  3125.     }
  3126.     
  3127.     /* Security */
  3128.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameSecurity );
  3129.     if ( stringDifference == nil )
  3130.     {
  3131.         (*theCGIHdl)->role = CGIRole_Security;
  3132.         return;
  3133.     }
  3134.     
  3135.     /* Error */
  3136.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameError );
  3137.     if ( stringDifference == nil )
  3138.     {
  3139.         (*theCGIHdl)->role = CGIRole_Error;
  3140.         return;
  3141.     }
  3142.     
  3143.     /* NoAccess */
  3144.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameNoAccess );
  3145.     if ( stringDifference == nil )
  3146.     {
  3147.         (*theCGIHdl)->role = CGIRole_NoAccess;
  3148.         return;
  3149.     }
  3150.     
  3151.     /* Index */
  3152.     stringDifference = strcmp ( (*theCGIHdl)->action, kCGIActionNameIndex );
  3153.     if ( stringDifference == nil )
  3154.     {
  3155.         (*theCGIHdl)->role = CGIRole_Index;
  3156.         return;
  3157.     }
  3158.     
  3159.     /* some unrecognized role - default to CGI */
  3160.     (*theCGIHdl)->role =CGIRole_CGI;
  3161.     
  3162.     #endif /* kCompilingForWSAPI */
  3163. } /* cgiSetRole */
  3164. #endif
  3165.  
  3166.  
  3167. /**  CLEAN UP  **/
  3168. #pragma mark -
  3169.  
  3170. /*  */
  3171. static void
  3172. cgiPostProcess ( CGIHdl theCGIHdl )
  3173. {
  3174.     CustomCGIPostProcess ( theCGIHdl );
  3175. } /* cgiPostProcess */
  3176.  
  3177.  
  3178. /**  Memory Allocation Cleanup  **/
  3179.  
  3180. /*  */
  3181. static void
  3182. cgiDisposeRecord ( CGIHdl theCGIHdl )
  3183. {
  3184.     #if kCompileWithExtraQC
  3185.     QCErr    theQCErr;
  3186.     #endif
  3187.     #if !(kCompilingForWSAPI)
  3188. //    OSErr    theErr;
  3189.     #endif
  3190.     
  3191.     my_assert ( theCGIHdl != NULL, "\pcgiDisposeRecord: theCGIHdl is NULL" );
  3192.     my_assert ( *theCGIHdl != NULL, "\pcgiDisposeRecord: *theCGIHdl is NULL" );
  3193.     
  3194.     /* the following is a bunch of statements checking if a parameter has been
  3195.         allocated and disposing of it if it has */
  3196.     
  3197.     #if kCompileWithCGIpath_args
  3198.     if ( (*theCGIHdl)->path_args != NULL )
  3199.     {
  3200.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->path_args) );
  3201.     }
  3202.     #endif
  3203.     #if kCompileWithCGIhttp_search_args
  3204.     if ( (*theCGIHdl)->http_search_args != NULL )
  3205.     {
  3206.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->http_search_args) );
  3207.     }
  3208.     #endif
  3209.     #if kCompileWithCGIpost_args
  3210.     if ( (*theCGIHdl)->post_args != NULL )
  3211.     {
  3212.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->post_args) );
  3213.     }
  3214.     #endif
  3215.     #if kCompileWithCGIscript_name
  3216.     if ( (*theCGIHdl)->script_name != NULL )
  3217.     {
  3218.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->script_name) );
  3219.     }
  3220.     #endif
  3221.     #if kCompileWithCGIreferer
  3222.     if ( (*theCGIHdl)->referer != NULL )
  3223.     {
  3224.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->referer) );
  3225.     }
  3226.     #endif
  3227.     
  3228.     #if kCompileWithCGIActionSupport
  3229.     if ( (*theCGIHdl)->action_path != NULL )
  3230.     {
  3231.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->action_path) );
  3232.     }
  3233.     #endif
  3234.     
  3235.     #if kCompileWithCGIfull_request
  3236.     if ( (*theCGIHdl)->full_request != NULL )
  3237.     {
  3238.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->full_request) );
  3239.     }
  3240.     #endif
  3241.     
  3242.     #if kCompileWithCGIfileMIMEType
  3243.     if ( (*theCGIHdl)->fileMIMEType != NULL )
  3244.     {
  3245.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->fileMIMEType) );
  3246.     }
  3247.     #endif
  3248.     
  3249.     #if kCompileWithCGIserverField
  3250.     if ( (*theCGIHdl)->serverField != NULL )
  3251.     {
  3252.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->serverField) );
  3253.     }
  3254.     #endif
  3255.     
  3256.     #if kCompileWithCGIserverDirectoryPath
  3257.     if ( (*theCGIHdl)->serverDirectoryPath != NULL )
  3258.     {
  3259.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->serverDirectoryPath) );
  3260.     }
  3261.     #endif
  3262.     
  3263.     #if kCompileWithCGIurlPhysicalPath
  3264.     if ( (*theCGIHdl)->urlPhysicalPath != NULL )
  3265.     {
  3266.         CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->urlPhysicalPath) );
  3267.     }
  3268.     #endif
  3269.     
  3270.     #if kCompileWithCGIFormHandling
  3271.     if ( (*theCGIHdl)->formFields != NULL )
  3272.     {
  3273.         CGIFormFieldsDispose ( theCGIHdl, (*theCGIHdl)->formFields );
  3274.     }
  3275.     #endif
  3276.     
  3277.     #if kCompilingForWSAPI
  3278.     
  3279.     #else
  3280.     if ( (*theCGIHdl)->appleEvent.descriptorType != nil )
  3281.     {
  3282. //•        HLock ( (Handle)theCGIHdl );
  3283. //•        theErr = AEDisposeDesc ( &((*theCGIHdl)->appleEvent) );
  3284. //•        HUnlock ( (Handle)theCGIHdl );
  3285.         //• deal with any errs?
  3286.     }
  3287. //    if ( (*theCGIHdl)->replyEvent.descriptorType != nil )
  3288. //    {
  3289. //        theErr = AEDuplicateDesc ( theReply, &((*theCGIHdl)->replyEvent) );
  3290. //        //• handle err?
  3291. //    }
  3292.     #endif
  3293.     
  3294.     if ( (*theCGIHdl)->responseData != NULL )
  3295.     {
  3296.         #if kCompileWithCGIResponseDataAsHandle
  3297.             CGIDisposeHandle ( theCGIHdl, (*theCGIHdl)->responseData );
  3298.         #else
  3299.             CGIDisposePtr ( theCGIHdl, (Ptr)((*theCGIHdl)->responseData) );
  3300.         #endif
  3301.     }
  3302.     
  3303.     CGIDisposeHandle ( theCGIHdl, (Handle)theCGIHdl );
  3304.     
  3305.     #if kCompileWithExtraQC
  3306.     theQCErr = QCBlockBoundsCheckNow ();
  3307.     theQCErr = QCHeapCheckNow ();
  3308.     #endif
  3309. } /* cgiDisposeRecord */
  3310.  
  3311.  
  3312. #endif    /* kCompileWithCGICode */
  3313.  
  3314. /***  EOF  ***/
  3315.